如何测试/模拟这个静态工厂(用于图形构建)



因此,我已经实现了我的业务类,在那里我通过构造函数传递所有依赖项,这样我就可以模拟这些依赖项并轻松地对它们进行单元测试。到目前为止,这很好,但在某一点上,我需要从这些对象中创建一个对象图。为此,我使用了一个静态工厂(遗憾的是,我不能使用DI框架)。示例:

public class FooBar {
    public FooBar(Foo foo, Bar bar) {
        this.foo = foo;
        this.bar = bar;
    }
}
public class Foo  {
    public Foo() {}
}
public class Bar {
    public Bar(Foo foo) {
        this.foo = foo;
    }
}
public class GraphFactory {
    public static FooBar newFooBar() {
        Foo foo = new Foo();
        Bar bar = new Bar(foo);
        return new FooBar(foo, bar);
    }
}

所以,我不能真正测试GraphFactory(不能模拟依赖关系),这有点好(这里没有做太多工作)。但是,如果图的构造更复杂,即它涉及到查找一些属性、执行JNDI查找等等,该怎么办?

我应该还是不为此写一个单元测试吗?我的班级设计可能坏了吗?单元测试的目的不是孤立地测试类吗?

这个设计没有任何问题,它是完全可测试的。如果我们关注工厂的单一责任——它负责组装对象图——那么测试是直接的:

  1. 设置工厂及其先决条件
  2. 调用工厂方法
  3. 断言对象是根据您的需求构建的:设置了默认值等

在上面的例子中,foo、bar和foobar是测试的结果,不需要模拟。

如果对象图的组装更复杂,并且您需要额外的服务来获取数据和检查应用程序设置,那么猜猜会发生什么?这些依赖关系通过其构造函数传递到工厂中。您应该模拟这些依赖关系,以将工厂与其依赖关系的依赖关系隔离开来。你工厂的消费者将通过他们的建设者获得一个完全有线的工厂;或者工厂在应用程序启动时组装并作为单体提供,等等

DI就是这样工作的。这听起来很奇怪,因为现在你必须担心工厂是如何创建的(以及谁创建了那个物体,等等,一直到乌龟),这是一种完全自然的反应。

这就是为什么我们有DI框架来组装复杂的对象图。在一个设计良好的DI应用程序中,不应该知道图形是如何组装的。在这个设计中,对DI框架应该一无所知。

唯一的例外是…(卷筒)。。。工厂里的东西!

如果被解析的对象在其构造函数中使用DI,那么大多数DI框架都会将DI容器注入到对象中。这大大简化了工厂的管道,并满足了我们的设计原则:除了我们的工厂,没有人知道如何构建我们的对象,我们已经将DI容器的知识封装到了负责对象组装的应用程序的关键区域。

Whew。现在,如果你还在关注,这会如何改变我们的测试?它不应该,至少不应该太多:步骤#1略有变化,在这里用mock对象填充DI容器,然后用DI容器构建工厂。

出于品味,有些人喜欢在测试期间使用自动模拟DI容器,当请求项目时,这些容器将自动生成模拟依赖关系。这可以消除大部分设置的疼痛。

如果newFooBar方法必须更复杂,则可以将除了创建FooBar之外的所有内容重构为包专用初始化方法。然后编写初始化方法的测试。

public class GraphFactory {
     public static FooBar newFooBar() {
         Foo foo = new Foo();
         Bar bar = new Bar(foo);
         FooBar toReturn = new FooBar(foo, bar);
         initialise( toReturn );
         return toReturn;
     }
     static void initialise( FooBar toInitialise ){
          // some stuff here that you can test
     }
} 

您不能模拟或存根静态方法调用,但您可以在没有模拟的情况下为工厂编写单元测试。

但是,为什么要使用静态工厂方法?。如果您想以静态方式访问工厂,最好将工厂存储在静态变量中。

您应该了解Dependency Injection,它将允许您在正确使用时摆脱工厂。我向你推荐这段视频作为介绍,它对我帮助很大:http://code.google.com/p/google-guice/

这是对谷歌库Guice的介绍,但它将帮助您理解DI。然后,您将看到如何用注释替换所有这些"新",让库为您完成所有工作。有些人会推荐Sring,但我发现Guice对初学者更友好。我只是希望这不会再次引发Guice与Spring的讨论:D

最新更新