【问题标题】:Writing maintainable unit tests with mock objects使用模拟对象编写可维护的单元测试
【发布时间】:2010-06-29 08:20:35
【问题描述】:

这是我正在为其编写单元测试的类的简化版本

class SomeClass {

    void methodA() {
        methodB();
        methodC();
        methodD();
    }

    void methodB() {
        //does something
    }

    void methodC() {
        //does something
    }

    void methodD() {
        //does something
    }
}

在为这个类编写单元测试时,我使用每个方法中使用的 EasyMock 模拟了对象。设置模拟对象及其期望很容易 在方法 B、C 和 D 中。但是要测试方法 A,我必须设置更多的模拟对象及其期望。此外,我正在不同条件下测试方法 A,这意味着我必须以不同的期望多次设置模拟对象。

最后,我的单元测试变得难以维护并且非常混乱。我想知道是否有人已经或看到了解决此问题的好方法。

【问题讨论】:

    标签: unit-testing mocking easymock


    【解决方案1】:

    如果我正确理解您的问题,我认为这是设计问题。单元测试的好处是编写测试通常会迫使您使您的设计更好。如果您在测试一个方法时需要模拟太多东西,这通常意味着您应该将您的类拆分为两个更小的类,这将更容易测试(以及编写、维护、错误修复和重用等)。

    在您的情况下,方法 A 似乎比方法 A、B、C 处于更高级别。您可以考虑将其删除到更高级别的类中,该类将包装 SomeClass:

    class HigherLevelClass {
        ISomeClass someClass;
    
        public HigherLevelClass(ISomeClass someClass)
        {
            this.someClass = someClass;
        }
    
        void methodA() {
            someClass.methodB();
            someClass.methodC();
            someClass.methodD();
        }
    }
    
    class SomeClass : ISomeClass {
        void methodB() {
            //does something
        }
    
        void methodC() {
            //does something
        }
    
        void methodD() {
            //does something
        }
    }
    

    现在,当您测试 methodA 时,您只需要模拟小的 ISomeClass 接口和三个方法调用。

    【讨论】:

      【解决方案2】:

      您可以将通用设置代码提取到单独的(可能是参数化的)方法中,然后在适当的时候调用它们。如果 methodA 的测试与其他方法的测试有很大不同,则 @Before 方法本身可能没有太多可放入的内容,因此您需要从测试方法本身调用适当的设置辅助方法组合。还是有点麻烦,但比到处复制代码要好。

      根据您使用的单元测试框架,可能还有其他选项,但以上应该适用于任何框架。

      【讨论】:

        【解决方案3】:

        这是Fragile test 的示例,因为模拟设置对SUT 的了解太深入了。

        我不知道 EasyMock,但是使用 Moq 你不需要设置 void 方法。但是,对于 Moq,方法必须是公共的或受保护的和虚拟的。

        【讨论】:

          【解决方案4】:

          对于您正在编写的每个测试,请考虑对该测试有价值的行为。您将设置一些行为所依赖的上下文,以及作为您想要验证的行为的结果的一些结果。

          设置相关上下文,验证结果,并将 NiceMocks 用于其他一切。

          我更喜欢默认情况下以这种方式工作的 Mockito (Java) 或 Moq (.NET)。这是 Mockito 关于 Mockito 与 EasyMock 的页面,因此您可以了解这个想法(在 Mockito 出现之前,EasyMock 没有 NiceMock):

          http://code.google.com/p/mockito/wiki/MockitoVSEasyMock

          您或许可以以类似的方式使用 EasyMock 的 NiceMock。希望这将帮助您解开您的测试。您可以随时导入这两个框架并将它们一起使用/如果有帮助,可以逐步切换。

          祝你好运!

          【讨论】:

            【解决方案5】:

            我在不同的条件下测试方法 A,这意味着我必须以不同的期望多次设置模拟对象。

            如果您关心 methodA 正在做什么以及必须调用哪个协作函数,那么您必须设置不同的期望...我不明白您如何可以跳过这一步?!

            如果您 testLogout,您会期望调用 myCollaborator.logout(),否则如果您 testLogin,您会期望调用 myCollaborator.login()。

            如果您有许多方法具有很多/不同的期望,那么可能会将您的班级分成合作者

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-06-07
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多