如果我已经有存根,模拟库是否多余?
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
范围内)...
在上面的单元测试中也使用 mock 是否多余?
中的值(如果有)是多少] 在上面的单元测试中做这样的事情?
...
...
@Mock
private Recruiter recR;
...
...
public void testGrowTeam( ) {
...
expect( recR.srcEmployee( blah) ).andReturn( blah )...
// exercising/assertions/validations as usual...
}
...
您可以放心地假设 RecruiterStandIn
完成了被测 class 出于上述单元测试的目的所需要的一切。也就是说,为了简单 answers/explanations,没有必要通过人为的 what-ifs 围绕存根维护和诸如此类的东西使上述场景过于复杂。
提前致谢。
我对你具体问题的回答:
- 在上面的单元测试中也使用 mock 是否多余?
由于单元测试是现在编写的,所以它是多余的,但我在下面看到了对你第二个问题的回答。
- 在上面的单元测试中另外做这样的事情会有什么价值(如果有的话)?
它认为这是您应该进行测试的方式,我建议摆脱您的存根,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 测试,您可以确保汽车以正确和预期的方式装满。
假设我在各自的来源中有以下 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
范围内)...
在上面的单元测试中也使用 mock 是否多余?
中的值(如果有)是多少] 在上面的单元测试中做这样的事情?
...
...
@Mock
private Recruiter recR;
...
...
public void testGrowTeam( ) {
...
expect( recR.srcEmployee( blah) ).andReturn( blah )...
// exercising/assertions/validations as usual...
}
...
您可以放心地假设 RecruiterStandIn
完成了被测 class 出于上述单元测试的目的所需要的一切。也就是说,为了简单 answers/explanations,没有必要通过人为的 what-ifs 围绕存根维护和诸如此类的东西使上述场景过于复杂。
提前致谢。
我对你具体问题的回答:
- 在上面的单元测试中也使用 mock 是否多余?
由于单元测试是现在编写的,所以它是多余的,但我在下面看到了对你第二个问题的回答。
- 在上面的单元测试中另外做这样的事情会有什么价值(如果有的话)?
它认为这是您应该进行测试的方式,我建议摆脱您的存根,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 测试,您可以确保汽车以正确和预期的方式装满。