I have the following code for a method that performs an action on a Callable parameter:
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();
}
I am using Mockito to try to validate that the line query.call()
is attempted exactly MAX_TRIES
number of times before throwing a RandomException
. I try to do this with the following test code:
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);
}
}
The Mockito code successfully tests that a RandomException
is thrown by the method, but improperly says that the test passes.
This test should fail because I write that Mockito should verify that mockCallable.call()
is performed at most once, but as far as I understand, it is called MAX_TRIES
times (which is set to 3).
Could someone explain this behavior and give advice on how to properly test the number of times mockCallable.call()
is invoked?
There are plenty of things wrong with your test. Here is something that does what you want:
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();
}
(please note: I changed the exception types for my test; but that should be obvious).
So, things you got wrong:
@RunWith(MockitoJUnitRunner.class)
, and then just go with @Mock annotations. You have the @Mock annotation plus a setup method that calls Mockito.mock() for the same field. That is redundant. Either use the annotation together with @RunWith, or call MockitoAnnotations.initMocks() in your setup method verify()
has to be called after you interacted with your mock object. Just think of that mock having counters. Before you interact, the counters are all 0. So there is no point asking a mock about its counters before you trigger the calls to the mock.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.