【问题标题】:Checking the results of a Factory in a unit test在单元测试中检查工厂的结果
【发布时间】:2010-09-07 10:18:09
【问题描述】:

我开发了一些具有相似行为的类,它们都实现了相同的接口。我实现了一个创建适当对象并返回接口的工厂。我正在为工厂编写单元测试。你得到的只是对象的接口。 测试工厂是否正常工作的最佳方法是什么?

我想知道Java的答案,但如果有跨语言的解决方案我想知道。

答案中的第二个,会像其他答案一样完成吗?如果是这样,我也会将另一个答案标记为已接受,并改写我的问题以解决返回接口的工厂并且您不知道实现该接口的具体类的类型,以及您确实知道具体类是什么的情况用过。

【问题讨论】:

    标签: java unit-testing tdd


    【解决方案1】:

    你要做的不是单元测试

    如果您测试返回的对象是否是特定具体类的实例,则不是单元测试。你是集成测试。虽然集成测试很重要,但它不是一回事。

    在单元测试中,您只需要测试对象本身。如果你断言返回的抽象对象的具体类型,你正在测试返回对象的实现。

    一般对象的单元测试

    在单元测试时,有四件事要断言:

    1. 查询(非 void 方法)的返回值是您所期望的。
    2. 命令的副作用(void 方法)会按照您的预期修改对象本身。
    3. 接收发送到其他对象的命令(这通常使用模拟来完成)。

    此外,您只想测试可以从对象实例(即公共接口)观察到的内容。否则,您将自己与一组特定的实现细节联系在一起。这将要求您在这些细节发生变化时更改您的测试。

    单元测试工厂

    对工厂进行单元测试真的很无趣,因为你对返回的查询对象的行为不感兴趣。该行为(希望)在其他地方进行了测试,可能是在对该对象本身进行单元测试时进行的。您真正感兴趣的只是返回的对象是否具有正确的类型,这在您的程序编译时得到保证。

    由于工厂不会随着时间而改变(因为那时它们将是“建造者”,这是另一种模式),因此没有要测试的命令。

    工厂负责实例化对象,因此它们不应依赖其他工厂为它们执行此操作。它们可能依赖于 Builder,但即便如此,我们也不应该测试 Builder 的正确性,只测试 Builder 是否收到消息。

    这意味着您对工厂的所有测试都是它们是否将消息发送到它们所依赖的对象。如果您使用依赖注入,这几乎是微不足道的。只需在单元测试中模拟依赖项,并验证它们是否接收到消息。

    单元测试工厂总结

    1. 不要测试返回对象的行为和实现细节!您的工厂不负责对象实例的实现!
    2. 测试是否收到发送到依赖项的命令。

    就是这样。如果没有依赖项,则无需测试。除了可能断言返回的对象不是null 引用。

    集成测试工厂

    如果您要求返回的抽象对象类型是特定具体类型的实例,那么这属于集成测试。

    这里的其他人已经回答了如何使用 instanceof 运算符来做到这一点。

    【讨论】:

    • 另外,这里是 Sandi Metz 关于单元测试的精彩演讲:youtube.com/watch?v=URSWYvyc42M
    • 如何对工厂自身构建的对象进行单元测试,然后“收到发送到其他对象的命令”?假设工厂确实返回new SomeObject(someDependency)。这实际上是一个逻辑,它不像模拟构造函数那样可测试——即使在某些测试框架下是可能的——而是“不不”。
    • @topr 那段逻辑应该在返回的类自己的测试中进行测试,而不是通过工厂。
    • 在返回类测试的范围内是测试与注入的协作者的交互。然而,提供它们是工厂的责任,并且这个逻辑也应该被测试。
    • 我有一个创建处理器链的工厂(ChainOfResponsibility 模式)。每个链段都有自己的 UnitTest。如果我不打算测试工厂,我怎么能保证链是正确构建的?
    【解决方案2】:

    由于我不知道你的工厂方法是什么样的,我现在只能建议

    1. 检查对象是否是您正在寻找的正确具体实现:

      IMyInterface fromFactory = factory.create(...);  
      Assert.assertTrue(fromFactory instanceof MyInterfaceImpl1);
      
    2. 您可以检查工厂是否使用有效的实例变量设置了具体实例。

    【讨论】:

      【解决方案3】:

      @cem-catikkas 我认为比较 getClass().getName() 值会更正确。在 MyInterfaceImpl1 类是子类的情况下,您的测试可能会被破坏,因为子类是 MyInterfaceImpl1 的 instanceof。我会改写如下:

      IMyInterface fromFactory = factory.create(...);  
      Assert.assertEquals(fromFactory.getClass().getName(), MyInterfaceImpl1.class.getName());
      

      如果您认为这可能会以某种方式失败(我无法想象),请进行两次验证。

      【讨论】:

      • 我同意,但我不认为检查类名的相等性是多余的。只检查类就足够了。
      • 为什么要测试 Class 中的方法返回相等的值,而不是直接比较 Class 对象?
      【解决方案4】:
      if (myNewObject instanceof CorrectClass)
      {
          /* pass test */
      }
      

      更新:

      不知道为什么这个被标记了,所以我会稍微扩展一下......

      public void doTest()
      {
          MyInterface inst = MyFactory.createAppropriateObject();
          if (! inst instanceof ExpectedConcreteClass)
          {
              /* FAIL */
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果您的工厂返回一个具体实例,您可以使用 @Parameters 注释以获得更灵活的自动单元测试。

        package it.sorintlab.pxrm.proposition.model.factory.task;
        
        import org.junit.Test;
        
        import java.util.Arrays;
        import java.util.Collection;
        
        import org.junit.runner.RunWith;
        import org.junit.runners.Parameterized;
        import org.junit.runners.Parameterized.Parameters;
        
        import static org.junit.Assert.*;
        
        @RunWith(Parameterized.class)
        public class TaskFactoryTest {
        
            @Parameters
            public static Collection<Object[]> data() {
                return Arrays.asList(new Object[][] {
                        { "sas:wp|repe" , WorkPackageAvailabilityFactory.class},
                        { "sas:wp|people", WorkPackagePeopleFactory.class},
                        { "edu:wp|course", WorkPackageCourseFactory.class},
                        { "edu:wp|module", WorkPackageModuleFactory.class},
                        { "else", AttachmentTaskDetailFactory.class}
                });
            }
        
            private String fInput;
            private Class<? extends TaskFactory> fExpected;
        
            public TaskFactoryTest(String input, Class<? extends TaskFactory> expected) {
                this.fInput = input;
                this.fExpected = expected;
            }
        
            @Test
            public void getFactory() {
                assertEquals(fExpected, TaskFactory.getFactory(fInput).getClass());
            }
        }
        

        这个例子是使用 Junit4 制作的。您可以注意到,只需一行代码,您就可以测试您的 Factory 方法的所有结果。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多