我读过各种关于测试中模仿和存根的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但我仍然不理解其中的区别。
当前回答
Stub
我相信最大的区别是您已经编写了带有预定行为的存根。所以你会有一个实现依赖的类(很可能是抽象类或接口),你是为了测试目的而伪造的,方法只是用设置的响应来存根。它们不会做任何花哨的事情,而且您应该已经在测试之外为它编写了存根代码。
Mock
模拟是测试的一部分,你必须根据你的期望来设置。mock并不是预先设定好的,所以您可以在测试中使用它。模拟在某种程度上是在运行时确定的,因为设置期望的代码必须在它们执行任何操作之前运行。
mock和stub的区别
使用模拟编写的测试通常遵循初始化->设置期望->练习->验证模式进行测试。而预先编写的存根将遵循初始化->练习->验证。
mock和stub之间的相似性
这两种方法的目的都是为了消除对类或函数的所有依赖项的测试,从而使您的测试在试图证明的内容方面更加集中和简单。
其他回答
Stub
存根是用来伪造具有预先编程行为的方法的对象。为了避免不必要的副作用(例如,存根可能会进行一个虚假的获取调用,返回预先编程的响应,而不实际向服务器发出请求),您可能会使用这个方法来代替现有的方法。
Mock
mock是用来模拟具有预编程行为和预编程期望的方法的对象。如果这些期望没有得到满足,那么mock将导致测试失败(例如,mock可以进行一个虚假的获取调用,返回预先编程的响应,而不实际向服务器发出请求,例如,第一个参数是“http://localhost:3008/”,否则测试将失败。)
区别
与模拟不同,存根没有预先编程的可能导致测试失败的期望。
存根用于您在测试中设置的具有预期返回值的方法。 mock用于void方法,这些方法在调用时的Assert中进行验证。
在codeschool.com的课程《Rails僵尸测试》中,他们给出了这些术语的定义:
Stub
用于将方法替换为返回指定结果的代码。
Mock
带有调用方法的断言的存根。
因此,正如Sean Copenhaver在他的回答中所描述的那样,不同之处在于mock设置了期望(即做出断言,关于是否或如何调用它们)。
Stub
存根是保存预定义数据并在测试期间使用它应答调用的对象。当您不能或不想涉及与真实数据对应或具有不良副作用的对象时,可以使用它。
一个例子可以是需要从数据库获取一些数据以响应方法调用的对象。我们引入了存根而不是实际对象,并定义了应该返回什么数据。
Stub的例子:
public class GradesService {
private final Gradebook gradebook;
public GradesService(Gradebook gradebook) {
this.gradebook = gradebook;
}
Double averageGrades(Student student) {
return average(gradebook.gradesFor(student));
}
}
不是从Gradebook store调用数据库来获取真实的学生成绩,而是预先配置将返回的成绩存根。定义足够的数据来测试平均计算算法。
public class GradesServiceTest {
private Student student;
private Gradebook gradebook;
@Before
public void setUp() throws Exception {
gradebook = mock(Gradebook.class);
student = new Student();
}
@Test
public void calculates_grades_average_for_student() {
//stubbing gradebook
when(gradebook.gradesFor(student)).thenReturn(grades(8, 6, 10));
double averageGrades = new GradesService(gradebook).averageGrades(student);
assertThat(averageGrades).isEqualTo(8.0);
}
}
Mock
mock是注册它们接收到的调用的对象。在测试断言中,您可以在mock上验证是否执行了所有预期的操作。当您不想调用产品代码或没有简单的方法来验证预期的代码是否被执行时,您可以使用模拟。没有返回值,也没有检查系统状态更改的简单方法。调用电子邮件发送服务的功能就是一个例子。
您不希望每次运行测试时都发送电子邮件。此外,在测试中验证发送的电子邮件是否正确并不容易。您唯一能做的就是验证在我们的测试中执行的功能的输出。在其他情况下,验证是否调用了电子邮件发送服务。
Mock示例:
public class SecurityCentral {
private final Window window;
private final Door door;
public SecurityCentral(Window window, Door door) {
this.window = window;
this.door = door;
}
void securityOn() {
window.close();
door.close();
}
}
你不想关上真正的门来测试安全方法是否有效,对吧?相反,在测试代码中放置门和窗模拟对象。
public class SecurityCentralTest {
Window windowMock = mock(Window.class);
Door doorMock = mock(Door.class);
@Test
public void enabling_security_locks_windows_and_doors() {
SecurityCentral securityCentral = new SecurityCentral(windowMock, doorMock);
securityCentral.securityOn();
verify(doorMock).close();
verify(windowMock).close();
}
}
非常感谢michaowallipski的好文章。欲进一步阅读:
测试双-马丁福勒https://martinfowler.com/bliki/TestDouble.html 测试Double - xUnit模式http://xunitpatterns.com/Test%20Double.html 嘲笑不是存根-马丁福勒https://martinfowler.com/articles/mocksArentStubs.html 命令查询分离- Martin Fowler https://martinfowler.com/bliki/CommandQuerySeparation.html
非常清楚和实际:
Stub:实现要伪造的类/对象的方法的类或对象,并且总是返回你想要的东西。
JavaScript示例:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Mock:与存根相同,但是它增加了一些逻辑,当一个方法被调用时“验证”,这样你就可以确定某个实现正在调用该方法。
正如@mLevan所说,假设你正在测试一个用户注册类。在调用Save之后,它应该调用SendConfirmationEmail。
一个非常愚蠢的代码
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
推荐文章
- 我如何使用Jest模拟JavaScript的“窗口”对象?
- 如何检查动态附加的事件监听器是否存在?
- 强制重新测试或禁用测试缓存
- 比较Java中2个XML文档的最佳方法
- 如何模拟低带宽、高延迟的环境?
- 使用Moq验证方法调用
- 我如何“休眠”Dart程序
- 使用Mockito的泛型“any()”方法
- Mockito中检测到未完成的存根
- 尝试模拟datetime.date.today(),但不工作
- 如何用python timeit对代码段进行性能测试?
- 确定bash中是否存在一个函数
- 如何使用“测试”包打印Go测试?
- 如何在IntelliJ中为整个项目配置“缩短命令行”方法
- toBe(true) vs toBeTruthy() vs toBeTrue()