背景:在开发中遇到一个问题,项目中禁止适用PowerMockito,当然了,我也非常不喜欢适用PowerMockito,除了对测试用例给予过多的便利,同时在mock一些静态方法,尤其是三方件,会有可能因为一些奇奇怪怪的原因,导致一堆mock报错,处理起来非常麻烦。
但是在这个用例中,在处理一个对象时,需要每次生成对象,都让这个对象的某个方法的表现是被替代的。
这个时候很多人会想到:
Person person = Mockito.spy(Person.class);
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Mockito.when(person.say()).thenReturn("mock for say.");
然后就可以只对每个Person对象的say方法进行mock,而其他方法执行实际代码逻辑。
接下来,尝试仅使用Mockito的方式进行这个实践
首先观察Mockito的方法,有两个方法名与构造函数有关:mockConstruction、mockConstructionWithAnswer
接下来尝试mockConstruction,发现如何mock,都无法实现上面的效果,没有被Mockito.when的方法默认是doNothing策略,导致无法达成测试用例的目的
Mockito.mockConstruction(Person.class, (person, context) -> {
// 各种mock尝试
});
那么只能用mockConstructionWithAnswer继续进行尝试,
方法签名:
public static <T> MockedConstruction<T> mockConstructionWithAnswer(
Class<T> classToMock, Answer defaultAnswer, Answer... additionalAnswers) {
return mockConstruction(
classToMock,
context -> {
if (context.getCount() == 1 || additionalAnswers.length == 0) {
return withSettings().defaultAnswer(defaultAnswer);
} else if (context.getCount() >= additionalAnswers.length) {
return withSettings()
.defaultAnswer(additionalAnswers[additionalAnswers.length - 1]);
} else {
return withSettings()
.defaultAnswer(additionalAnswers[context.getCount() - 2]);
}
},
(mock, context) -> {});
}
观察方法签名,显然需要我们实现一个answer进行传入,通过观察,invocation对象拥有方法的对象,以及方法的对应入参,那么可以使用类反射的机制直接突破困境,如下面的代码
Person person = Mockito.spy(Person.class);
Mockito.when(person.say()).thenReturn("mock for say.");
Mockito.mockConstructionWithAnswer(Person.class, new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return invocation.getMethod().invoke(person, invocation.getArguments());
}
});