【问题标题】:Is A Mocking Library Redundant If I Already Have a Stub?如果我已经有一个存根,模拟库是多余的吗?
【发布时间】:2015-09-17 20:25:33
【问题描述】:

假设我在各自的源文件夹/包中有以下类...

[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. 在上面的单元测试中也使用模拟会是多余的吗?

  2. 另外在以上单元测试?

...

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

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

您可以放心地假设RecruiterStandIn 为上述单元测试的目的完成了被测类所要求的一切。也就是说,为了简单的答案/解释,没有必要将上面的场景过度复杂化,围绕维护存根和诸如此类的人为的 what-ifs

提前致谢。

【问题讨论】:

    标签: java unit-testing mocking data-access-layer


    【解决方案1】:

    我对您的具体问题的回答:

    1. 在上面的单元测试中也使用模拟是多余的吗?

    由于现在编写单元测试,这将是多余的,但我在下面看到你第二个问题的答案。

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

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

    @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 为您提供了剔除某些部分的能力以及更多功能,而无需您创建新的测试实体。

    原答案

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

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

    public class Car {
          public void fuelUp()
    }
    

    现在通过标准单元测试,我们将在调用fuelUp() 后检查汽车是否充满了油,并且从司机那里扣除了适当的金额。但由于我们从未测试过fuelUp() 与周围世界的交互方式,它很可能会执行以下操作:

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

    但是通过模拟对象测试,您可以确保 Car 以正确和预期的方式被填满。

    【讨论】:

    • 感谢 tkeE2036。我已经被卖掉了。但同样感谢你的传福音。我仍然希望能回答我的问题的症结——我认为你已经忽略了。
    • 如果您觉得我的帖子有帮助,请考虑接受它以帮助其他人:)
    • 冒着听起来忘恩负义的风险,如果我的问题是“我应该进行模拟对象测试吗?”那你就赚到钱了。如果我的重点是“在路上”的某个时间,那么你会打一千个。然而,事实上,我现在只关注 above 示例。鉴于 above 示例,我特别询问使用模拟是否是多余的。仅限。
    • 正如我在帖子中所说的那样,按照现在所写的那样,这将是多余的。我接着说即使你不想进行模拟测试,我也会摆脱你的存根并使用框架来创建你的存根。
    • 谢谢 tkeE2036。精彩的后续!我阅读了您对 Q2 的回答,因为“价值是,您不必为了返回一些预定义数据而维护两个类”。正确的?但是您指的是哪个“两个类”? Recruiter 必须留下。 Employee 必须留下。我可以看到失去一节课 (RecruiterStandIn)。但是,您认为哪一个是您第二次失去的?
    猜你喜欢
    • 2017-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 2015-07-25
    相关资源
    最近更新 更多