我读过各种关于测试中模仿和存根的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但我仍然不理解其中的区别。
当前回答
前言
有几个对象的定义是不真实的。一般的术语是双重测试。这个术语包括:dummy, fake, stub, mock。
参考
根据Martin Fowler的文章:
Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example). Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'. Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.
风格
模拟vs存根=行为测试vs状态测试
原则
根据每个测试只测试一件事的原则,一个测试中可能有几个存根,但一般只有一个mock。
生命周期
使用存根测试生命周期:
准备正在测试的对象和它的存根合作者。 锻炼——测试功能。 验证状态——使用断言来检查对象的状态。 Teardown -清理资源。
使用mock测试生命周期:
设置数据-准备正在测试的对象。 设置期望-在mock中准备主对象使用的期望。 锻炼——测试功能。 验证期望——验证在mock中调用了正确的方法。 验证状态——使用断言来检查对象的状态。 Teardown -清理资源。
总结
模拟测试和存根测试都给出了这个问题的答案:结果是什么?
使用模拟进行测试也感兴趣:结果是如何实现的?
其他回答
我偶然看到了《小嘲笑者鲍勃叔叔》的一篇有趣的文章。它以一种非常容易理解的方式解释了所有的术语,因此对初学者很有用。Martin fowler的文章很难读,尤其是对于像我这样的初学者。
存根用于您在测试中设置的具有预期返回值的方法。 mock用于void方法,这些方法在调用时的Assert中进行验证。
从论文模拟角色,而不是对象,由jMock的开发人员:
存根是返回罐装的产品代码的虚拟实现 结果。Mock对象充当存根,但也包括到的断言 测量目标对象与其邻居的交互作用。
所以,主要的区别是:
在存根上设置的期望通常是通用的,而在mock上设置的期望可能更“聪明”(例如,在第一次调用时返回this,在第二次调用时返回this等)。 存根主要用于设置SUT的间接输入,而mock可用于测试SUT的间接输入和间接输出。
综上所述,同时也试图驱散福勒文章标题中的困惑:mock是存根,但它们不仅仅是存根。
假设你有一个名为EmployeeService的类,你想测试它,并且它对一个名为EmployeeDao的接口有一个依赖:
public class EmployeeService{
private EmployeeDao dao;
public EmployeeService(Dao dao){this.dao = dao;}
public String getEmployeeName(int id){
Employee emp = bar.goToDatabaseAndBringTheEmployeeWithId(id);
return emp != null?emp.getFullName:null;
}
//Further state and behavior
}
public interface EmployeeDao{
Employee goToDatabaseAndBringTheEmployeeWithId(int id);
}
在测试类内部:
public class EmployeeServiceTest{
EmployeeService service;
EmployeeDao mockDao = Mockito.mock(EmployeeDao.class);//Line 3
@Before
public void setUp(){
service = new EmployeeService(mockDao);
}
//Tests
//....
}
在上面的测试类的第3行中,我们对mock框架(在本例中是Mockito)说“嘿,Mockito,给我做一个具有EmployeeDao功能的对象。”框架将创建一个对象,它有goToDatabaseAndBringTheEmployeeWithId方法,但实际上没有主体。你的工作就是指导那个mock做什么。这是一个嘲弄。
但是你也可以创建一个实现EmployeeDao接口的类,并在测试类中使用它:
public EmployeeDaoStub implements EmployeeDao{
public Employee goToDatabaseAndBringTheEmployeeWithId(int id){
//No trip to DB, just returning a dummy Employee object
return new Employee("John","Woo","123 Lincoln str");
}
}
在你的测试类中,这次使用stub而不是mock:
public class EmployeeServiceTest{
EmployeeService service;
EmployeeDao daoStub = new EmployeeDaoStub();//Line 3
@Before
public void setUp(){
service = new EmployeeService(daoStub);
}
//Tests
//....
}
因此,为了包装这一切,存根是您(或其他人)专门创建的类,以模仿某些依赖关系,只是为了拥有所需的状态。是的,正如其他人所说,它主要是关于一个状态,而mock通常是由mock框架创建的,您不知道它的内部是什么样子的。但是使用存根,您知道将得到什么类:它是您创建的类。
哦,顺便说一下,如果你的依赖项是一个类而不是一个接口,你可以扩展这个类来创建你的存根。
Mock只是测试行为,确保调用了特定的方法。 Stub是特定对象的可测试版本(本质上)。
你说的苹果方式是什么意思?
推荐文章
- ReferenceError: description没有定义NodeJs
- Mock vs MagicMock
- 我如何使用Jest模拟JavaScript的“窗口”对象?
- 如何检查动态附加的事件监听器是否存在?
- 强制重新测试或禁用测试缓存
- 比较Java中2个XML文档的最佳方法
- 如何模拟低带宽、高延迟的环境?
- 使用Moq验证方法调用
- 我如何“休眠”Dart程序
- 使用Mockito的泛型“any()”方法
- Mockito中检测到未完成的存根
- 尝试模拟datetime.date.today(),但不工作
- 如何用python timeit对代码段进行性能测试?
- 确定bash中是否存在一个函数
- 如何使用“测试”包打印Go测试?