何时期望和何时存根?(When to Expect and When to Stub?)

2019-06-25 13:40发布

我用NMock2,我已经起草了以下NMock类来表示一些常见的模拟框架概念:

  • Expect :指定一个嘲笑方法应该返回什么,并说必须发生通话或测试失败(伴随调用时VerifyAllExpectationsHaveBeenMet()

  • Stub :指定一个嘲笑方法应该返回什么,但不能导致测试失败。

所以,应该什么时候做?

Answer 1:

很多嘲讽框架正在把嘲弄和存根的概念更接近与紧靠在一起,他们可以考虑在功能上几乎相同的位置。 但是,从概念上讲,我平时尽量遵守这个约定:

  • 模拟 :只有当你明确地尝试验证对象的测试行为(即你的测试是说这个对象必须调用该对象)。
  • 存根 :当你试图测试某些功能/行为,但为了得到那工作,你需要依靠一些外部对象(即你的测试是说这个对象必须做一些事情,但它的副作用,它可以调用该对象)

当您确保这变得更加清晰,每个单元测试只测试一两件事。 当然,如果你尝试测试在一次测试中的一切,那么你不如期望的一切。 但只期待的事情,具体的单元测试是检查,你的代码更清晰,因为你可以一眼看出哪些测试的目的是看到。

这样做的另一个好处是,你会稍微改变从绝缘及获得更好的错误消息,当一个变化会导致一个突破。 换句话说,如果你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,但此时你在一个单元测试来测试两种情况。 这将使你的测试更脆弱(因为有事情会改变,打破测试更大的表面积),并不太清楚(因为当合并测试失败它不是立即明显的问题是什么..不正常的增加,或者是审计工作不?)



Answer 2:

“期待的动作,存根查询”。 如果呼叫要改变世界的状态的对象之外的测试,然后使它的期望 - 你在乎它就会怎么叫。 如果它只是一个查询,可以一次或六次调用它不改变系统的状态,那么存根调用。

还有一两件事,注意区分是存根和期望之间,也就是个人通话,不一定是整个对象。



Answer 3:

嗯......恕我直言,它不能简单:如果您的测试是要确保你的演示将调用保存,做一个期待。 如果您的测试是要确保你的演示将处理异常优雅,如果保存抛出了,做一个存根。

有关详细信息,请参阅本播客由Hanselman在和Osherove (艺术单元测试的作者)



文章来源: When to Expect and When to Stub?