我用NMock2,我已经起草了以下NMock类来表示一些常见的模拟框架概念:
Expect
:指定一个嘲笑方法应该返回什么,并说必须发生通话或测试失败(伴随调用时VerifyAllExpectationsHaveBeenMet()
Stub
:指定一个嘲笑方法应该返回什么,但不能导致测试失败。
所以,应该什么时候做?
我用NMock2,我已经起草了以下NMock类来表示一些常见的模拟框架概念:
Expect
:指定一个嘲笑方法应该返回什么,并说必须发生通话或测试失败(伴随调用时VerifyAllExpectationsHaveBeenMet()
Stub
:指定一个嘲笑方法应该返回什么,但不能导致测试失败。
所以,应该什么时候做?
很多嘲讽框架正在把嘲弄和存根的概念更接近与紧靠在一起,他们可以考虑在功能上几乎相同的位置。 但是,从概念上讲,我平时尽量遵守这个约定:
当您确保这变得更加清晰,每个单元测试只测试一两件事。 当然,如果你尝试测试在一次测试中的一切,那么你不如期望的一切。 但只期待的事情,具体的单元测试是检查,你的代码更清晰,因为你可以一眼看出哪些测试的目的是看到。
这样做的另一个好处是,你会稍微改变从绝缘及获得更好的错误消息,当一个变化会导致一个突破。 换句话说,如果你subtley改变你的实现的某些部分,你更有可能得到的只有一个测试用例打破,它会告诉你什么是坏了,而不是整个的测试打破&只是制造噪音套件。
编辑 :基于一个人为的例子,其中一个计算器对象审核所有添加到数据库中(在伪代码)这可能是更清晰...
public void CalculateShouldAddTwoNumbersCorrectly() {
var auditDB = //Get mock object of Audit DB
//Stub out the audit functionality...
var calculator = new Calculator(auditDB);
int result = calculator.Add(1, 2);
//assert that result is 3
}
public void CalculateShouldAuditAddsToTheDatabase() {
var auditDB = //Get mock object of Audit DB
//Expect the audit functionality...
var calculator = new Calculator(auditDB);
int result = calculator.Add(1, 2);
//verify that the audit was performed.
}
因此,在第一次测试情况下,我们正在测试的功能, Add
方法与不在乎审计事件是否发生,但我们碰巧知道计算器不会制定出一个AUDITDB参考,所以我们只是存根它给我们最小的功能,让我们的具体测试案例的工作。 在第二次测试我们特别测试,当你做的Add
,审计事件发生,所以在这里我们使用的预期(请注意,我们甚至不关心结果是什么,因为这不是我们正在测试)。
是的,你可以这两种情况合并成一个,和做的期望和主张,你的结果是3,但此时你在一个单元测试来测试两种情况。 这将使你的测试更脆弱(因为有事情会改变,打破测试更大的表面积),并不太清楚(因为当合并测试失败它不是立即明显的问题是什么..不正常的增加,或者是审计工作不?)
“期待的动作,存根查询”。 如果呼叫要改变世界的状态的对象之外的测试,然后使它的期望 - 你在乎它就会怎么叫。 如果它只是一个查询,可以一次或六次调用它不改变系统的状态,那么存根调用。
还有一两件事,注意区分是存根和期望之间,也就是个人通话,不一定是整个对象。
嗯......恕我直言,它不能简单:如果您的测试是要确保你的演示将调用保存,做一个期待。 如果您的测试是要确保你的演示将处理异常优雅,如果保存抛出了,做一个存根。
有关详细信息,请参阅本播客由Hanselman在和Osherove (艺术单元测试的作者)