注释@InjectMocks
为我们提供了存根/填充私有成员并重用测试用例的方法。这是概念代码,当我们对假冒成员进行填充时,问题就出现了。
public class TestBuilder{
@Spy
private StubComponent componentA = new StubComponent();
@Mock
private FakeComponent componentB;
@InjectMocks
private class TestTarget targetInstance = mock(TestTarget.class);
public static Class TestTarget{
private StubComponent componentA;
private FakeComponent componentB;
public ShimmedResultB testInvokation(String para){
componentA.doCallRealMethod();
ShimmedResultA shimmedResultA = componentA.someUnableToStubbedMethod(para);
ShimmedResultB shouldNotBeNull = componentB.someShimmedMethod(shimmedResultA);
return shouldNotBeNull;
}
}
private TestBuilder(){
MockitoAnnotations.initMocks(this);
//Shim the real component A with partial stubbed
doReturn(shimmedResultA).when(componentA).someUnableToStubbedMethod(any());
//Shim the fake component B
//************The issue is here****************
componentB = mock(FakeComponent.class);
//*********************************************
when(componentB.someShimmedMethod(any())).thenReturn(shimmedResultB);
}
public TestTarget getTargetInstance(){
return this.targetInstance;
}
public static TestTarget build(){
return (new TestBuilder()).getTargetInstance();
}
public static main(String[] args){
TestTarget testInstance = TestBuilder.build();
ShimmedResultB result = testInstance.testInvokation("");
assertThat(result, not(equalTo(null)));
}
}
问题在于我们嘲笑假componentB
。然后someShimmedMethod
将返回null。似乎InjectMocks
无法将mock()
传递给私人会员。
以下是一些术语定义:
StubComponent:测试将作为私有成员渗透到此组件。但是,有一些方法可能无法通过。我们可以采用其公共方法。该组件可能具有较小的依赖性范围,很容易由本地资源启动。
FakeComponent:此组件将在其他地方进行测试。在这里,我们只能构建模拟实例并填充测试目标将利用的所有方法。
存根:@Spy可以帮助我们勾住Stubbed成员。私人会员并非100%真实。但是一些短暂的部分可以让测试渗透到这个私人成员中。
Shim:@Mock会在initMocks之前给我们一个空指针。所以我们可以在initMocks之后开始设计Fake组件的返回。这是@InjectMocks的神奇之处。然而,这是最棘手的部分,因为Developer想要直观地启动componentB的每一件事并模拟(FakeComponent.class)。这将清除所有的垫片设计并使你的断言失败。
=============================================== ===================
感谢Maciej的回答,并在翻译我的测试用例结构时抱歉。让我向Maciej的答案提出更清晰的描述。
public class TestBuilder{
@Spy
private StubComponent componentA = new StubComponent();
@Mock
private FakeComponent componentB;
@InjectMocks
private TestTarget targetInstance = mock(TestTarget.class);
public static Class TestTarget{
private StubComponent componentA;
private FakeComponent componentB;
public ShimmedResultB testInvokation(String para){
componentA.doCallRealMethod();
ShimmedResultA shimmedResultA = componentA.someUnableToStubbedMethod(para);
ShimmedResultB shouldNotBeNull = componentB.someShimmedMethod(shimmedResultA);
return shouldNotBeNull;
}
public TestTarget(){
//The FakeComponent has some specific remote resource
//And could not be initialized here
componentB = new FakeComponent();
//We will use mock server to test this FakeComponent else where
}
}
private TestBuilder(){
//Hook the testing Function for trigger the step in
doCallRealMethod().when(this.targetInstance).testInvokation(anyString());
//Inject Stubbed and Faked Private Member for testing
MockitoAnnotations.initMocks(this);
//Shim the real component A with partial stubbed
doReturn(shimmedResultA).when(componentA).someUnableToStubbedMethod(any());
//************The issue is here****************
componentB = mock(FakeComponent.class);
//*********************************************
//Shim the leveraged method of fake componentB
when(componentB.someShimmedMethod(any())).thenReturn(shimmedResultB);
}
public TestTarget getTargetInstance(){
return this.targetInstance;
}
public static TestTarget build(){
return (new TestBuilder()).getTargetInstance();
}
public static main(String[] args){
TestTarget testInstance = TestBuilder.build();
//The doRealCall hook will trigger the testing
ShimmedResultB result = testInstance.testInvokation("");
assertThat(result, not(equalTo(null)));
}
}
第二个概念代码中添加了一些内容:
componentB是我们不想介入的范围。但是,TestTarget在其构造函数中启动了componentB。当我们有一个与远程源相关的实用程序时,这很常见。我们使用模拟服务器或其他技术独立测试componentB。因此,我们只能使用mock(TestTarget.class)。
因为我们嘲笑了TestTarget。有一件事我错过了我们需要使用doCallRealMethod()。when(targetInstance)来触发testInvokation()。这限制了targetInstance的null声明。我们需要mock()并挂钩doCallRealMethod。
因此,结果是我们需要将@Mock保留为null而不使用任何mock()让@InjectMocks来处理填充程序。 我们发现使用@InjectMocks时这很棘手。
答案 0 :(得分:0)
问题在于@InjectMocks定义:
@InjectMocks
private class TestTarget targetInstance = mock(TestTarget.class);
测试中的类永远不应该是mock(也就是class关键字的原因)。
尝试使用:
@InjectMocks
private TestTarget targetInstance = new TestTarget();
或简单地说:
@InjectMocks
private TestTarget targetInstance;