我读过各种关于测试中模仿和存根的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但我仍然不理解其中的区别。
当前回答
存根和模拟都覆盖外部依赖项,但区别在于
stub ->用于测试数据
mock ->用于测试行为
不测试任何东西(只是用空方法覆盖功能,例如替换Logger以避免在测试时记录任何噪音)
其他回答
存根用于您在测试中设置的具有预期返回值的方法。 mock用于void方法,这些方法在调用时的Assert中进行验证。
存根是一个空函数,用于在测试期间避免未处理的异常:
function foo(){}
mock是一个人为的函数,用于避免在测试期间对操作系统、环境或硬件的依赖:
function foo(bar){ window = this; return window.toString(bar); }
在断言和状态方面:
在事件或状态更改之前断言模拟 存根不被断言,它们在事件之前提供状态,以避免执行来自不相关单元的代码 间谍就像存根一样被设置,然后在事件或状态改变后被断言 假的是不断言的,他们运行后的事件硬编码的依赖,以避免状态
参考文献
极客词汇:Mock 极客词汇:存根 极客术语:间谍 测试替身:假的,模拟和存根
Stub
存根是用来伪造具有预先编程行为的方法的对象。为了避免不必要的副作用(例如,存根可能会进行一个虚假的获取调用,返回预先编程的响应,而不实际向服务器发出请求),您可能会使用这个方法来代替现有的方法。
Mock
mock是用来模拟具有预编程行为和预编程期望的方法的对象。如果这些期望没有得到满足,那么mock将导致测试失败(例如,mock可以进行一个虚假的获取调用,返回预先编程的响应,而不实际向服务器发出请求,例如,第一个参数是“http://localhost:3008/”,否则测试将失败。)
区别
与模拟不同,存根没有预先编程的可能导致测试失败的期望。
mock既是技术对象,也是功能对象。
这个模拟是技术性的。它确实是由一个模拟库(EasyMock、JMockit和最近的Mockito都以这些库而闻名)创建的,这要归功于字节代码生成。 生成模拟实现的方式是,我们可以在调用方法时将其设置为返回特定值,但也可以执行其他一些事情,例如验证模拟方法是否使用某些特定参数(严格检查)或任何参数(不严格检查)调用。
实例化一个mock:
@Mock Foo fooMock
记录行为:
when(fooMock.hello()).thenReturn("hello you!");
验证调用:
verify(fooMock).hello()
这些显然不是实例化/重写Foo类/行为的自然方式。这就是为什么我提到技术方面。
但是mock也是功能性的,因为它是我们需要从SUT中隔离出来的类的实例。有了记录在上面的行为,我们就可以在SUT中使用它就像使用存根一样。
存根只是一个函数对象:它是我们需要从SUT中隔离出来的类的实例,仅此而已。 这意味着在单元测试期间需要的存根类和所有行为fixture都必须显式地定义。 例如,存根hello()将需要子类化Foo类(或实现它拥有的接口)并重写hello():
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
如果另一个测试场景需要另一个返回值,我们可能需要定义一个通用的方法来设置返回值:
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
其他场景:如果我有一个副作用方法(没有返回),并且我要检查该方法是否被调用,我可能应该在存根类中添加一个布尔值或计数器来计算该方法被调用的次数。
结论
存根通常需要为单元测试编写大量开销/代码。mock因为提供了开箱即用的记录/验证功能而阻止了什么。 这就是为什么现在随着优秀的模拟库的出现,存根方法很少在实践中使用。
关于Martin Fowler文章:当我使用mock和避免存根时,我不认为我是一个“嘲笑主义者”程序员。 但是当真正需要时我使用mock(讨厌的依赖关系),当我测试带有依赖关系的类时,我喜欢测试切片和迷你集成测试,而mock将是一种开销。
这张幻灯片很好地解释了主要的区别。
*选自CSE 403第16讲,华盛顿大学(幻灯片由“Marty Stepp”制作)
推荐文章
- ReferenceError: description没有定义NodeJs
- Mock vs MagicMock
- 我如何使用Jest模拟JavaScript的“窗口”对象?
- 如何检查动态附加的事件监听器是否存在?
- 强制重新测试或禁用测试缓存
- 比较Java中2个XML文档的最佳方法
- 如何模拟低带宽、高延迟的环境?
- 使用Moq验证方法调用
- 我如何“休眠”Dart程序
- 使用Mockito的泛型“any()”方法
- Mockito中检测到未完成的存根
- 尝试模拟datetime.date.today(),但不工作
- 如何用python timeit对代码段进行性能测试?
- 确定bash中是否存在一个函数
- 如何使用“测试”包打印Go测试?