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

时间:2012-02-15 09:14:09

标签: java unit-testing junit mocking

所以,我已经实现了我的业务类,我通过构造函数传递所有依赖项,所以我可以嘲笑那些并轻松地对它们进行单元测试。到目前为止,这很有用,但是,有一点,我需要从这些对象中创建一个对象图。为此,我正在使用一个静态工厂(遗憾的是我不能使用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查找等等呢?

我还应该为此编写单元测试吗?我的班级设计可能会被打破吗?单元测试的目的不是单独测试类吗?

4 个答案:

答案 0 :(得分:2)

如果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
     }
} 

答案 1 :(得分:2)

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

但是,为什么要使用静态工厂方法?如果要以静态方式访问它,可能更好地将工厂存储在静态变量中。

答案 2 :(得分:2)

这种设计没有任何问题,而且完全可以测试。如果我们专注于工厂的单一责任 - 它负责组装对象图 - 对此的测试是直截了当的:

  1. 根据先决条件设置工厂。
  2. 调用工厂方法。
  3. 断言对象是根据您的要求构建的:设置了默认值等。
  4. 在上面的例子中,foo,bar和foobar是测试的结果,不需要模拟。

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

    这就是DI的工作原理。这听起来很奇怪,因为现在你不得不担心工厂是如何被创造的(以及谁创造了这个对象等,乌龟一直都是如此),这是一个完全自然的反应。

    这就是为什么我们有DI框架来组装复杂的对象图。在精心设计的DI应用程序中,什么都不应该知道图表是如何组装的。类似于这种设计,任何人都不应该知道DI框架。

    该规则的唯一例外是......(鼓点)......工厂对象!

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

    呼。现在,如果您仍在继续,这将如何改变我们的测试?它不应该,至少不要太多:步骤#1稍微改变,用模拟对象填充DI容器,然后使用DI容器构建工厂。

    作为一种品味,有些人喜欢在测试期间使用自动模拟DI容器,这会在请求项目时自动生成模拟依赖项。这可以消除大部分痛苦。

答案 3 :(得分:1)

你应该看看Dependecy Injection,它可以让你在正确使用时摆脱工厂。我推荐你这个视频作为介绍,它帮助了我很多: http://code.google.com/p/google-guice/

这是谷歌图书馆Guice的介绍,但它会帮助你理解DI。然后你会看到如何用注释替换所有那些'new',让图书馆为你完成所有的工作。有些人会推荐Sring,但我觉得Guice对初学者更友好。我只是希望这不会再次引发Guice vs Spring的讨论:D