我在单元测试中做了一些根本错误的事情吗?

时间:2010-08-30 13:28:50

标签: unit-testing mocking

在阅读了一篇关于单元测试行为而不是状态的有趣文章之后,我开始意识到我的单元测试通常与我的代码紧密耦合,因为我正在使用模拟。 我不能在没有模拟的情况下对单元测试进行成像,但事实是这些模拟将我的单元测试与我的代码非常相似,因为期望和返回调用。

例如,当我创建一个使用模拟的测试时,我会记录对特​​定模拟的所有调用并分配返回值。 现在当我因任何原因更改实际代码的实现时,很多测试都会中断,因为模拟不希望这个调用,也迫使我更新单元测试,并有效地强迫我实现每次更改两次...... 这种情况发生了很多。

这个问题是否是使用模拟所固有的,我应该学会忍受它,还是我做了一些根本错误的事情? 请赐教:) 当然,非常欢迎明确的解释说明。

4 个答案:

答案 0 :(得分:5)

  

当我创建一个使用模拟的测试时,   我记录所有特定的电话   模拟并分配返回值

听起来你可能过度指定期望。

尝试在测试中尽可能少地设置代码:存根(而不是期望)与当前测试无关的所有行为,并仅指定使测试工作绝对需要的返回值。

This answer包含一个简明的例子(以及另一个更详细的解释)。

答案 1 :(得分:4)

我的经验是仅在(子)系统的bounderies上使用模拟。如果我有两个强相关的类,我不会嘲笑它们,而是一起测试它们。一个例子可能是复合和访客。如果我测试一个具体的访问者,我不会使用模拟复合而是创建真正的复合材料。有人可能会说这不是单元测试(取决于什么是单位的定义)。但那并不重要。我试图实现的是:

  1. 编写可读测试(没有模拟的测试大多数时候都更容易阅读)。
  2. 仅测试焦点的代码区域(在示例中为concreate visitor和复合的相关部分)。
  3. 编写快速测试(只要我只实例化几个类,在示例中具体复合,这不是一个问题......注意传递创作)。
  4. 只有遇到子系统的边界时才使用模拟。示例:我有一个可以将自身渲染为渲染器的复合体如果我测试复合体的渲染逻辑,我会模拟渲染器。

    测试行为而不是状态看起来最初提出,但总的来说,我会测试状态,因为生成的测试很容易维护。模拟是一门大炮。不要用大锤敲打坚果。

答案 2 :(得分:3)

如果您正在修理测试,因为它们已经破坏,那么您没有按预期使用它们。

如果方法的行为发生变化,在测试驱动开发中,您首先要更改测试以期望新行为,然后实现新行为。

答案 3 :(得分:0)

这里有几个好的答案,但对我来说,一个好的经验法则是测试方法的要求,而不是实现。有时这可能意味着使用模拟对象,因为交互是必需的,但通常最好不要测试方法的返回值或对象状态的变化。