Mockito 验证 Callable 对象没有失败
Mockito verify not failing on Callable object
我有以下代码用于对 Callable 参数执行操作的方法:
public static <T> T queryWithRetry(Callable<T> query, int maxTries, int retryIntervalInMilliseconds) throws MongoServiceException, InterruptedException {
int tries = MAX_TRIES;
while (tries-- > 0) {
try {
return query.call();
} catch (TimeoutException e) {
LOGGER.debug(String.format("Query timed out. Retrying attempt %d/%d", MAX_TRIES - tries, MAX_TRIES));
Thread.sleep(RETRY_INTERVAL_IN_MILLISECONDS);
continue;
}
throw new RandomException();
}
我正在使用 Mockito 来尝试验证 query.call()
行在抛出 RandomException
之前被准确尝试了 MAX_TRIES
次。我尝试使用以下测试代码来执行此操作:
public class CallableQueryTest {
private static final int MAX_TRIES = 3;
private static final int RETRY_INTERVAL_IN_MILLISECONDS = 100;
@Mock
private Callable<Document> mockCallable;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws Exception {
mockCallable = Mockito.mock(Callable.class);
}
@Test
public void testQueryConfigThrowsRandomExceptionOnTimeout() throws Exception {
Mockito.when(mockCallable.call()).thenThrow(new TimeoutException("timeout"));
thrown.expect(RandomException.class);
Mockito.verify(mockCallable, Mockito.atMost(1)).call();
MongoQueryUtils.queryWithRetry(mockCallable, MAX_TRIES, RETRY_INTERVAL_IN_MILLISECONDS);
}
}
Mockito 代码成功测试该方法抛出 RandomException
,但错误地说测试通过。
这个测试应该失败,因为我写道 Mockito 应该验证 mockCallable.call()
最多执行一次,但据我所知,它被调用 MAX_TRIES
次(设置为 3 ).
有人可以解释这种行为并就如何正确测试 mockCallable.call()
被调用的次数提出建议吗?
您的测试有 很多 错误。这是您想要的东西:
private static final int MAX_TRIES = 3;
private static final int RETRY_INTERVAL_IN_MILLISECONDS = 100;
@Mock
private Callable<String> mockCallable;
@Test
public void testQueryConfigThrowsRandomExceptionOnTimeout() throws Exception {
when(mockCallable.call()).thenThrow(new IllegalArgumentException("timeout"));
try {
queryWithRetry(mockCallable, MAX_TRIES, RETRY_INTERVAL_IN_MILLISECONDS);
fail("should have thrown");
} catch (RuntimeException re) {
// as expected
}
verify(mockCallable, Mockito.times(3)).call();
}
(请注意:我更改了测试的异常类型;但这应该是显而易见的)。
所以,你错了:
- 我建议使用
@RunWith(MockitoJUnitRunner.class)
,然后使用@Mock 注释即可。您有 @Mock 注释 plus 一个为同一字段调用 Mockito.mock() 的设置方法。那是多余的。要么将注解与@RunWith 一起使用,要么在设置方法 中调用MockitoAnnotations.initMocks()
verify()
必须在 与模拟对象交互后 被调用。想想那个有计数器的模拟。在您交互之前,计数器都为 0。因此,在您触发对模拟的调用之前 询问模拟的计数器是没有意义的。
- 我不熟悉JUnit规则;因此我重写了测试以在没有它的情况下工作。如果要使用JUnit规则进行异常处理;很好地留给 reader 作为练习。
我有以下代码用于对 Callable 参数执行操作的方法:
public static <T> T queryWithRetry(Callable<T> query, int maxTries, int retryIntervalInMilliseconds) throws MongoServiceException, InterruptedException {
int tries = MAX_TRIES;
while (tries-- > 0) {
try {
return query.call();
} catch (TimeoutException e) {
LOGGER.debug(String.format("Query timed out. Retrying attempt %d/%d", MAX_TRIES - tries, MAX_TRIES));
Thread.sleep(RETRY_INTERVAL_IN_MILLISECONDS);
continue;
}
throw new RandomException();
}
我正在使用 Mockito 来尝试验证 query.call()
行在抛出 RandomException
之前被准确尝试了 MAX_TRIES
次。我尝试使用以下测试代码来执行此操作:
public class CallableQueryTest {
private static final int MAX_TRIES = 3;
private static final int RETRY_INTERVAL_IN_MILLISECONDS = 100;
@Mock
private Callable<Document> mockCallable;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws Exception {
mockCallable = Mockito.mock(Callable.class);
}
@Test
public void testQueryConfigThrowsRandomExceptionOnTimeout() throws Exception {
Mockito.when(mockCallable.call()).thenThrow(new TimeoutException("timeout"));
thrown.expect(RandomException.class);
Mockito.verify(mockCallable, Mockito.atMost(1)).call();
MongoQueryUtils.queryWithRetry(mockCallable, MAX_TRIES, RETRY_INTERVAL_IN_MILLISECONDS);
}
}
Mockito 代码成功测试该方法抛出 RandomException
,但错误地说测试通过。
这个测试应该失败,因为我写道 Mockito 应该验证 mockCallable.call()
最多执行一次,但据我所知,它被调用 MAX_TRIES
次(设置为 3 ).
有人可以解释这种行为并就如何正确测试 mockCallable.call()
被调用的次数提出建议吗?
您的测试有 很多 错误。这是您想要的东西:
private static final int MAX_TRIES = 3;
private static final int RETRY_INTERVAL_IN_MILLISECONDS = 100;
@Mock
private Callable<String> mockCallable;
@Test
public void testQueryConfigThrowsRandomExceptionOnTimeout() throws Exception {
when(mockCallable.call()).thenThrow(new IllegalArgumentException("timeout"));
try {
queryWithRetry(mockCallable, MAX_TRIES, RETRY_INTERVAL_IN_MILLISECONDS);
fail("should have thrown");
} catch (RuntimeException re) {
// as expected
}
verify(mockCallable, Mockito.times(3)).call();
}
(请注意:我更改了测试的异常类型;但这应该是显而易见的)。
所以,你错了:
- 我建议使用
@RunWith(MockitoJUnitRunner.class)
,然后使用@Mock 注释即可。您有 @Mock 注释 plus 一个为同一字段调用 Mockito.mock() 的设置方法。那是多余的。要么将注解与@RunWith 一起使用,要么在设置方法 中调用MockitoAnnotations.initMocks()
verify()
必须在 与模拟对象交互后 被调用。想想那个有计数器的模拟。在您交互之前,计数器都为 0。因此,在您触发对模拟的调用之前 询问模拟的计数器是没有意义的。- 我不熟悉JUnit规则;因此我重写了测试以在没有它的情况下工作。如果要使用JUnit规则进行异常处理;很好地留给 reader 作为练习。