这是我正在为
编写单元测试的类的简化版本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,这意味着我必须多次设置模拟对象,并有不同的期望。
最后,我的单元测试变得难以维护并且非常混乱。我想知道是否有人有或看到过这个问题的良好解决方案。
答案 0 :(得分: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
}
}
现在当你测试方法时,你需要模拟的只是小的ISomeClass接口和三个方法调用。
答案 1 :(得分:0)
您可以将常见的设置代码提取到单独的(可能是参数化的)方法中,然后在适当的时候调用它们。如果methodA的测试与其他方法的测试具有非常不同的夹具,则可能没有太多内容放入@Before方法本身,因此您需要从测试方法本身调用设置帮助方法的适当组合。它仍然有点麻烦,但比在整个地方复制代码要好。
根据您使用的单元测试框架,可能还有其他选项,但上述内容适用于任何框架。
答案 2 :(得分:0)
这是Fragile test的一个示例,因为模拟设置对SUT有过深入的了解。
我不知道EasyMock,但是使用Moq你不需要设置void方法。但是,使用Moq,方法必须是公共的或受保护的和虚拟的。
答案 3 :(得分:0)
对于您正在编写的每个测试,请考虑对该测试有价值的行为。您将设置一些行为所依赖的上下文,以及您要验证的行为导致的某些结果。
设置相关的上下文,验证结果,并将NiceMocks用于其他所有内容。
我更喜欢Mockito(Java)或Moq(.NET),它默认以这种方式工作。这是关于Mockito与EasyMock的Mockito页面,所以你可以得到这个想法(EasyMock在Mockito出现之前没有NiceMock):
http://code.google.com/p/mockito/wiki/MockitoVSEasyMock
您可以以类似的方式使用EasyMock的NiceMock。希望这可以帮助您解决您的测试。您可以随时导入这两个框架并将它们一起使用/逐步切换,如果有帮助的话。
祝你好运!答案 4 :(得分:0)
我在不同的条件下测试方法A,这意味着我必须多次设置模拟对象,并有不同的期望。
如果您关心A正在做什么方法以及必须调用哪个协作者功能,那么您必须设置不同的期望...我不知道您如何跳过此步骤?!
如果你是testLogout,你会期望调用myCollaborator.logout(),否则如果你使用testLogin,你会期望像myCollaborator.login()。
如果您有很多方法有很多/不同的期望,可能会在合作者中分割您的课程