依赖注入如何促进可测试性 [英] How Dependency Injection Fosters Testability

查看:149
本文介绍了依赖注入如何促进可测试性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读工厂模式,并且发现了一些建议将Factory模式与依赖项注入结合使用,以最大程度地提高可重用性和可测试性.尽管我找不到该Factory-DI混合动力的任何具体示例,但我将尝试 try 并给出一些解释的代码示例.但是,我的问题真的是关于这种方法如何提高可测试性.

I've been reading up on the Factory pattern, and have come across articles that suggest using the Factory pattern in conjunction with dependency injection to maximize reusability and testability. Although I have not been able to find any concrete examples of this Factory-DI hybrid, I'm going to try and give some code examples of my interpretation. However, my question is really about how this approach improves testability.

所以我们有一个Widget类:

public class Widget {
    // blah
}

我们想包含一个WidgetFactory来控制Widget s的构造:

And we want to include a WidgetFactory to control the construction of Widgets:

public interface WidgetFactory {

    public abstract static Widget getWidget();
}

public class StandardWidgetFactory implements WidgetFactory {

    @Override
    public final static Widget getWidget() {
        // Creates normal Widgets
    }
}

public class TestWidgetFactory implements WidgetFactory {

    @Override
    public final static Widget getWidget() {
        // Creates test/mock Widgets for unit testing purposes
    }
}

尽管此示例使用Spring DI(这是我有经验的唯一API),但是我们谈论的是Guice还是任何其他IoC框架都没有关系.这里的想法是,我们现在要在运行时注入正确的WidgetFactory实现,这取决于我们是测试代码还是正常运行.在春季,bean的配置可能如下所示:

Although this example uses Spring DI (that's the only API I have experience with), it doesn't really matter if we're talking about Guice or any other IoC framework; the idea here is that we're now going to inject the correct WidgetFactory implementation at runtime to depending on whether we are testing the code or running normally. In Spring the beans config might look like this:

<bean id="widget-factory" class="org.me.myproject.StandardWidgetFactory"/>
<bean id="test-widget-factory" class="org.me.myproject.TestWidgetFactory"/>

<bean id="injected-factory" ref="${valueWillBeStdOrTestDependingOnEnvProp}"/>

然后,在代码中:

WidgetFactory wf = applicationContext.getBean("injected-factory");
Widget w = wf.getWidget();

这样,一个环境(部署级别)变量(可能在某个地方的 .properties 文件中定义)决定了Spring DI将注入StandardWidgetFactory还是TestWidgetFactory.

This way, an environmental (deployment-level) variable, perhaps defined in a .properties file somewhere, decides whether Spring DI will inject a StandardWidgetFactory or a TestWidgetFactory.

我做对了吗?!?似乎很多基础架构都为我的Widget获得了良好的可测试性.并不是我反对它,但是对我来说,这感觉就像是过度设计.

Am I doing it right?!? This seems like an awful lot of infrastructure just obtain good testability for my Widget. Not that I'm opposed to it, but it just feels like over-engineering to me.

我之所以问,是因为我将在其他程序包中包含其他对象,这些程序包中的其他方法使用其中的Widget对象.也许像这样:

The reason why I am asking this is because I will have other objects in other packages, which have methods that use Widget objects inside of them. Perhaps something like:

public class Fizz {
    public void doSomething() {

        WidgetFactory wf = applicationContext.getBean("injected-factory");
        Widget widget = wf.getWidget();

        int foo = widget.calculatePremable(this.rippleFactor);

        doSomethingElse(foo);
    }
}

没有这种庞大的,看似过度设计的设置,我将无法将"mock Widgets"注入到我的Fizz::doSomething()单元测试中.

Without this huge, seemingly-over-engineered setup, there would be no way for me to inject "mock Widgets" into my unit test for Fizz::doSomething().

所以我很伤心:一方面,这感觉就像是我在思考事情-我可能会做的很好(如果我的解释是不正确).另一方面,我看不到任何干净的方法来解决它.

So I'm torn: on one end it just feels like I'm overthinking things - which I may very well be doing (if my interpretation is incorrect). On the other hand I don't see any clean way to get around it.

作为切线问题的话题,这也引起了我的另一个巨大担忧:如果我的解释是正确的(甚至有些正确),那么这是否意味着每个对象都需要Factories?!?

As a segue into a tangential question, this also raises another huge concern of mine: if my interpretation is correct (or even somewhat correct), then does this mean we need Factories for every object?!?

这听起来像是过度工程!截止点是什么?沙滩上的哪条线划定何时使用工厂,何时不使用工厂?感谢您的帮助和对一个冗长问题的歉意.只是我的头在旋转.

That sounds like way-overengineering! What's the cut-off? What's the line in the sand that demarcates when to use a Factory, and when not to?! Thanks for any help and apologies for a verbose question. It's just got my head spinning.

推荐答案

DI/IoC有助于测试,因为您可以轻松决定要使用的实现方式,而无需修改使用该实现的代码.这意味着您可以注入已知的实现来行使特定功能,例如,模拟Web服务故障,保证对功能的良好(或不良)输入等.

DI/IoC helps testing because you can decide, easily, what implementation to use, without modifying the code that uses it. This means you can inject a known implementation to exercise specific functionality, e.g., simulate a web service failure, guarantee good (or bad) input to a function, etc.

不需要工厂就可以进行DI/IoC.是否需要工厂完全取决于使用情况.

Factories are not required to make DI/IoC work. Whether or not a factory is required depends entirely on usage specifics.

public class Fizz {

    @Inject    // Guice, new JEE, etc. or
    @Autowired // Spring, or
    private Widget widget;

    public void doSomething() {
        int foo = widget.calculatePremable(this.rippleFactor);
        doSomethingElse(foo);
    }

}

这篇关于依赖注入如何促进可测试性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆