TDD和模拟测试

3
首先,我必须说,我对模拟不太熟悉。所以可能会有所遗漏。
我也刚开始习惯TDD方法。因此,在我的实际项目中,我正在处理业务层的类,而数据层尚未部署。我认为,现在是开始使用模拟的好时机。我正在使用Rhino Mocks,但遇到了需要在编写类本身之前了解其实现细节的问题。
Rhino Mocks检查是否实际调用了预期调用的所有方法。因此,我通常需要先知道被测试方法调用的模拟方法,即使这些方法可以按任何顺序调用。因此,我经常在测试之前编写复杂的方法,因为这样我就已经知道方法的调用顺序了。
简单示例:
public void CreateAandB(bool arg1, bool arg2) {
    if(arg1)
        daoA.Create();
    else throw new exception;
    if(arg2)
        daoB.Create();
    else throw new exception;
}

如果我想测试这个方法的错误处理,我首先需要知道调用哪个方法。但是当我写测试时,我不想被具体实现细节所困扰。 我是否有什么遗漏?
2个回答

4
你有两个选择。如果该方法应该导致类中的某些更改,那么您可以测试方法的结果。因此,您可以调用CreateAandB(true,false),然后调用其他方法来查看是否创建了正确的内容。在这种情况下,您的模拟对象可能只是提供一些数据的存根。
如果doaAdoaB是注入到您的类中的对象,实际上在DB或类似位置创建数据,而您无法验证测试结果,则需要测试与它们的交互。在这种情况下,您需要创建模拟对象并设置期望值,然后调用方法并验证期望是否得到满足。在这种情况下,您的模拟对象将是模拟对象,并验证预期行为。
是的,您正在测试实现细节,但您正在测试方法是否正确使用其依赖项的细节,这正是您想要测试的内容,而不是如何使用它们的细节,这些细节您实际上并不感兴趣。
IDao daoA = MockRepository.GenerateMock<IDao>(); //create mock
daoA.Expect(dao=>dao.Create); //set expectation

...

daoA.VerifyExpectations(); //check that the Create method was called

您可以确保期望按照一定顺序发生,但我认为不使用AAA语法(2009年的来源,可能已经改变,编辑在此处提供了一个可能有效的选项),但似乎有人开发了一种方法来实现这个目标在这里。我从未使用过并且无法验证。
至于需要知道哪个方法首先被调用以便验证异常,您有几个选择:
  • 在异常中使用不同的消息并检查以确定引发了哪个异常。
  • 除了期望异常之外,还期望调用daoA。如果没有调用daoA,则测试失败,因为异常必须是第一个。

daoA 和 daoB 被注入,它们在数据库中创建了某些内容。所以如果我想测试上面的例子,只测试异常处理是没有意义的吗?但这个问题并不是通过那种方式解决的。我必须知道 arg1 是否在 arg2 之前被测试过。 - myAces
假设doa实现了某个接口,那么您可以测试daoA是否调用了其“Create”方法,以及doaB是否也是如此。您可以通过在模拟对象上设置期望来实现这一点。我会在我的答案中添加一些细节。 - Sam Holder
我觉得我开始明白了。我会阅读你提供的链接并尝试你所提到的内容。谢谢你的帮助。 - myAces

3

很多时候你只需要虚假对象,而不是模拟对象。模拟对象用于测试组件交互,通常可以通过直接查询SUT的状态来避免这种情况。模拟对象的大多数实际用途是测试与某些外部系统(数据库、文件系统、Web服务等)的交互,对于其他事情,你应该能够直接查询系统状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接