如果我已经有存根,模拟库是否多余?

Is A Mocking Library Redundant If I Already Have a Stub?

假设我在各自的来源中有以下 classes folders/packages...

[src/myApp]
|_Employee «concrete»
|_Manager «abstract» 
|_ManagerImpl «concrete» (Class Under Test)
|_Recruiter «abstract» 
|_RecruiterImpl «concrete» (Collaborator)

...

public class ManagerImpl implements Manager {
   ...
   private Recruiter recR;
   ...

   public void growTeam( Object criteria ){ 
      //...check preconditions
      Employee newB = recR.srcEmployee( criteria );
      //...whatever else
   }

   ...

} 

...

[test/myApp]
|_RecruiterStandIn «concrete»
|_ManagerImplTest   

...

public class RecruiterStandIn implements Recruiter { 

   Map<Object, Employee> reSrcPool = new HashMap<>();

   public RecruiterStandIn( ){ 
      // populate reSrcPool with dummy test data...
   }    

   public Employee srcEmployee( Object criteria ){
      return reSrcPool.get( criteria );
   }
}

...

public class ManagerImplTest {
   ...
   // Class Under Test
   private ManagerImpl mgr;

   // Collaborator
   private Recruiter recR = new RecruiterStandIn( );
   ...

   public void testGrowTeam(  ) {
      //...
      mgr.setRecruiter( recR ); 
      mgr.growTeam( criteria );
      // assertions follow...
   }

   ...    
}

...

这是我的问题:假设我有一个 RecruiterStandIn 代码库中已经存在的具体实现用于测试目的(test 范围内)...

  1. 在上面的单元测试中也使用 mock 是否多余?

  2. 中的值(如果有)是多少] 在上面的单元测试中做这样的事情?

...

...
@Mock
private Recruiter recR;
...

...
public void testGrowTeam(  ) { 
   ...
   expect( recR.srcEmployee( blah) ).andReturn( blah )...
   // exercising/assertions/validations as usual...
}
...

您可以放心地假设 RecruiterStandIn 完成了被测 class 出于上述单元测试的目的所需要的一切。也就是说,为了简单 answers/explanations,没有必要通过人为的 what-ifs 围绕存根维护和诸如此类的东西使上述场景过于复杂。

提前致谢。

我对你具体问题的回答:

  1. 在上面的单元测试中也使用 mock 是否多余?

由于单元测试是现在编写的,所以它是多余的,但我在下面看到了对你第二个问题的回答。

  1. 在上面的单元测试中另外做这样的事情会有什么价值(如果有的话)?

它认为这是您应该进行测试的方式,我建议摆脱您的存根,RecruiterStandIn。相反,我会将新兵设置为 return 预设答案,这样你就不必维护两个 类 只是为了 return 一些预定义的数据:

@Spy
private Recruiter recR;

public void testGrowTeam(  ) { 

     // Setup canned data return
     doReturn(generateTestEmployee()).when(recR).srcEmployee(any(Object.class));

    expect( recR.srcEmployee( blah) ).andReturn( blah )...
    // exercising/assertions/validations as usual...
}

仅供参考,以上语法适用于使用 Mockito。据我所知,在您的案例中,Mockito 使您能够删除某些部分以及更多内容,而无需您创建新的测试实体。

原答案

当然可以,您应该进行模拟 object 测试。 Mocks Aren't Stubs。模拟 object 测试允许您测试 类 之间的交互,并确保事物与周围的世界正确交互。我认为当您第一次编写 类 及其相应测试时,这些测试的价值较小。模拟 object 测试在一年后大放异彩,当一个新开发人员进来时,你无意中破坏了代码,因为她不明白需要某些内部行为..

一个有点人为的例子是,假设我们有一辆需要加满油的汽车:

public class Car {
      public void fuelUp()
}

现在,通过标准单元测试,我们将检查在调用 fuelUp() 后汽车是否加满油以及是否从 driver 中扣除了适当的金额。但由于我们从未测试过 fuelUp() 是如何与周围的世界互动的,它很容易做以下事情:

public void fueldUp() {
    siphonGasFromNearestCar();
    buyCoffeeAndChips()
}

但是通过模拟 object 测试,您可以确保汽车以正确和预期的方式装满。