我读过各种关于测试中模仿和存根的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但我仍然不理解其中的区别。
当前回答
假设你有一个名为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——Mock拦截对一个方法或函数的调用(或者像模拟类那样的一组方法和函数)。它不是该方法或函数的替代品。在那次拦截中,mock可以做任何它想做的事情,比如记录输入和输出,决定短路调用,更改返回值,等等。
存根——存根是一个有效的方法或函数(或一组方法和函数,就像存根类一样)的完整工作实现,它与它存根的方法、函数或一组方法和函数具有相同的接口/签名。stub实现通常只会做在单元测试上下文中可以接受的事情,这意味着它不会做IO,同时模仿它要stub的东西的行为。
这张幻灯片很好地解释了主要的区别。
*选自CSE 403第16讲,华盛顿大学(幻灯片由“Marty Stepp”制作)
下面是对每一个的描述,然后是真实世界的样本。
Dummy - just bogus values to satisfy the API. Example: If you're testing a method of a class which requires many mandatory parameters in a constructor which have no effect on your test, then you may create dummy objects for the purpose of creating new instances of a class. Fake - create a test implementation of a class which may have a dependency on some external infrastructure. (It's good practice that your unit test does NOT actually interact with external infrastructure.) Example: Create fake implementation for accessing a database, replace it with in-memory collection. Stub - override methods to return hard-coded values, also referred to as state-based. Example: Your test class depends on a method Calculate() taking 5 minutes to complete. Rather than wait for 5 minutes you can replace its real implementation with stub that returns hard-coded values; taking only a small fraction of the time. Mock - very similar to Stub but interaction-based rather than state-based. This means you don't expect from Mock to return some value, but to assume that specific order of method calls are made. Example: You're testing a user registration class. After calling Save, it should call SendConfirmationEmail.
存根和Mock实际上是Mock的子类型,两者都交换了实际实现和测试实现,但出于不同的、特定的原因。
在codeschool.com的课程《Rails僵尸测试》中,他们给出了这些术语的定义:
Stub
用于将方法替换为返回指定结果的代码。
Mock
带有调用方法的断言的存根。
因此,正如Sean Copenhaver在他的回答中所描述的那样,不同之处在于mock设置了期望(即做出断言,关于是否或如何调用它们)。
Mock只是测试行为,确保调用了特定的方法。 Stub是特定对象的可测试版本(本质上)。
你说的苹果方式是什么意思?
推荐文章
- 比较Java中2个XML文档的最佳方法
- 如何模拟低带宽、高延迟的环境?
- 使用Moq验证方法调用
- 我如何“休眠”Dart程序
- 使用Mockito的泛型“any()”方法
- Mockito中检测到未完成的存根
- 尝试模拟datetime.date.today(),但不工作
- 如何用python timeit对代码段进行性能测试?
- 确定bash中是否存在一个函数
- 如何使用“测试”包打印Go测试?
- 如何在IntelliJ中为整个项目配置“缩短命令行”方法
- toBe(true) vs toBeTruthy() vs toBeTrue()
- 使用Mockito测试抽象类
- 什么时候应该使用Debug.Assert()?
- 使用Moq模拟扩展方法