我正在通过
static
方法调用外部系统
MyExternalServiceAccessor.myMethod(param1, param2);
到目前为止,我已经使用 PowerMockito
的
verifyStatic
进行了 单元测试,如下
import static org.mockito.Matchers.eq;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;
..
mockStatic(MyExternalServiceAccessor.class);
..
verifyStatic();
MyExternalService.myMethod(eq(arg1), eq(arg2));
我现在想要进行上述调用“异步”(best-effort使用fire-and-forget语义进行调用,我不关心响应);所以参考这里我把它包裹在
CompletableFuture.runAsync(..)
里面,如下
import java.util.concurrent.CompletableFuture;
CompletableFuture.runAsync(() -> {
MyExternalServiceAccessor.myMethod(param1, param2);
});
但是按照预期进行此更改后,单元测试变得不稳定,因为由于
MyExternalServiceAccessor.myMethod(..)
的异步调用,有时验证会失败并出现以下错误
java.lang.RuntimeException: Wanted but not invoked com.company.team.service.serviceutils.MyClass.myFunctionBeingUnitTested(
null,
null
);
Actually, there were zero interactions with this mock.
Mockito
支持超时验证对于非static
方法;是否有与 PowerMockito
方法相同的 Mockito
(或 static
)等效项?我认为我可能已经通过使用在我的静态方法调用之后进行的非静态方法调用作为“代理”找到了修复,以暗示我们的静态方法调用也一定已经发生了
我正在记录错误指标,以防我的静态方法调用失败,如下所示
import java.util.concurrent.CompletableFuture;
CompletableFuture.runAsync(() -> {
try {
MyExternalServiceAccessor.myMethod(param1, param2);
} catch (final Exception e) {
// here metricLogger is injected into MyClass from outside so can be mocked for tests
metricLogger.logCounter("MyExternalServiceAccessor.myMethod:failure", 1);
}
});
现在在上面的代码中
metricLogger.logCounter(..)
调用是一个非静态调用,我们可以利用mockito的超时验证MyExternalServiceAccessor.myMethod(..)
调用,因此我们可以确定,如果 logCounter
调用发生了,那么 myMethod
调用也一定发生了
myMethod
抛出异常因此,对于错误场景,我们会像这样编写测试,等待 100 毫秒,然后再验证调用的预期方法
verify(mockMetricLogger, timeout(100).times(1)).logCounter(eq("MyExternalServiceAccessor.myMethod:failure"), eq(1));
verifyStatic();
MyExternalService.myMethod(eq(arg1), eq(arg2));
对于非错误场景,我们可以使用这个线程中建议的技巧来等待并验证我们的模拟被称为“零”次,即,即使在等待后也没有被调用
verify(mockMetricLogger, timeout(100).times(0)).logCounter(eq("MyExternalServiceAccessor.myMethod:failure"), eq(1));
verifyStatic();
MyExternalService.myMethod(eq(arg1), eq(arg2));
到目前为止,我的测试已经通过,但由于(a)它们一开始并不太不稳定,(b)我们正在处理可能间歇性出现的臭名昭著的异步问题,我不确定是否没有错误片状现象是否已完全消除的情况。
(我当然可以完全消除不稳定的情况,例如在成功场景中发布一个计数器指标并将其用作代理,但现在还不想这样做)
我还必须测试整个代码块从未被调用的场景(由于上游条件表达式阻止流程到达触发异步块的这一点)
为此,我按照
此线程中的建议利用了
Mockito.never()
verify(mockMetricLogger, timeout(100).times(0)).logCounter(eq("MyExternalServiceAccessor.myMethod:failure"), eq(1));
verifyStatic(never());
MyExternalService.myMethod(eq(arg1), eq(arg2));