我读过各种关于测试中模仿和存根的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但我仍然不理解其中的区别。


当前回答

测试对象根据某些提示(函数调用)或其他刺激来执行动作。下面是测试情况的具体例子。

场景——EMT学生考试

一名学生学习成为一名紧急医疗技术员。如果你对这个测试情况不熟悉,可以去看Ian Gallagher在《无耻之徒》第六季第十集的节目。

为了测试目的,寻找各种疾病的患者太昂贵了。相反,我们使用角色。我们问测试对象(Ian)"当你到达现场发现病人已经不能动弹,失去知觉你首先要做什么?"伊恩回答说:“我检查现场是否安全。”测试指导老师说“现场是安全的”。

教师(和演员)能够向测试对象的问题注入任意答案。

在这里,教练(和演员)是一个模拟。医学培训与计算机科学家一样使用这个术语(例如模拟代码模拟)。

场景——注册一个网站

你正在测试雅虎,一个你听说过的新的电子邮件服务。为了注册,你必须提供你的生日和其他侵入性问题的答案。

该网站要求你年满21岁。输入1970年1月1日。它满足了需求,并且将您从实现记住我的生日并输入它的工作流的费力过程中拯救出来。

这个日期是存根。这个词的用法只适用于计算机科学。

其他回答

以下是我的理解……

如果您在本地创建测试对象并将其提供给本地服务,则使用的是模拟对象。 这将为您在本地服务中实现的方法提供测试。 它用于验证行为 当您从真正的服务提供者获得测试数据时(尽管是从接口的测试版本获得对象的测试版本),您是在使用存根 存根可以有逻辑来接受特定的输入并给出相应的输出来帮助您执行状态验证…

存根用于您在测试中设置的具有预期返回值的方法。 mock用于void方法,这些方法在调用时的Assert中进行验证。

模拟:帮助模拟和检查结果交互。这些交互 SUT调用它的依赖项来改变它们的状态。

存根:帮助模拟传入的交互。这些相互作用称为 SUT对其依赖项进行处理以获取输入数据。

来源:单元测试原则、实践和模式- Manning

假设你有一个名为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框架创建的,您不知道它的内部是什么样子的。但是使用存根,您知道将得到什么类:它是您创建的类。

哦,顺便说一下,如果你的依赖项是一个类而不是一个接口,你可以扩展这个类来创建你的存根。

我在阅读《单元测试的艺术》时,偶然发现了以下定义:

fake是一个通用术语,可以用来描述存根或模拟对象(手写或其他),因为它们看起来都像真实的对象。赝品是存根还是mock取决于它在当前测试中的使用方式。如果它用于检查交互(根据其断言),则它是一个模拟对象。否则,它就是存根。