我读过各种关于测试中模仿和存根的文章,包括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框架创建的,您不知道它的内部是什么样子的。但是使用存根,您知道将得到什么类:它是您创建的类。

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

其他回答

我偶然看到了《小嘲笑者鲍勃叔叔》的一篇有趣的文章。它以一种非常容易理解的方式解释了所有的术语,因此对初学者很有用。Martin fowler的文章很难读,尤其是对于像我这样的初学者。

使用一个心智模型确实帮助我理解了这一点,而不是所有的解释和文章,这些都不能“理解”。

想象一下,你的孩子桌子上有一个玻璃盘子,他开始玩它。现在,你害怕它会破裂。所以,你给了他一个塑料盘子。这将是一个Mock(相同的行为,相同的接口,“更软的”实现)。

现在,假设你没有塑料替代品,所以你解释说“如果你继续玩它,它会碎的!”这是一个Stub,您预先提供了一个预定义的状态。

哑巴就是他根本不用的叉子…间谍可能是提供你已经使用过的有效解释。

Mock只是测试行为,确保调用了特定的方法。 Stub是特定对象的可测试版本(本质上)。

你说的苹果方式是什么意思?

有很多很棒的答案,我喜欢这个,所以我把它做成了一个表格。

Dummy Stub Mock Fake
API O O O O
States X O O O
Values X X O O
Behavior X X X O

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

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