2

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

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,这意味着我必须以不同的期望多次设置模拟对象。

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

4

5 回答 5

3

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

在您的情况下,方法 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 接口和三个方法调用。

于 2010-06-29T10:51:44.323 回答
0

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

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

于 2010-06-29T08:25:31.477 回答
0

这是一个脆弱测试的例子,因为模拟设置对SUT的了解太深入了。

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

于 2010-06-29T08:40:36.907 回答
0

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

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

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

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

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

祝你好运!

于 2010-06-29T11:56:10.150 回答
0

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

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

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

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

于 2010-06-30T12:03:27.313 回答