2

I am writing an integration test against some code that asynchronously creates a record when a different value is updated in the database. I would like to check the state of the system after the record is created, verifying that it was created as expected. The test therefore needs to wait until the record is created.

I can use Mockito to create a spy for the function that creates the record. Mockito even has the option to wait for the method to be called via Mockito.timeout, giving up if a certain amount of time has elapsed without the method being called:

// Use or create/wire in spy. In my case, this is set up with @SpyBean from spring-boot-test.
RecordCreationService recordCreationServiceSpy = ...;

testClass.update(someValue);

Mockito.verify(recordCreationServiceSpy, Mockito.timeout(10_000)).createRecord(ArgumentMatchers.any());

However, this merely waits for the call to have started, not for it to have been completed. Thus, this enters a race condition where the verification can finish before the desired call completes.

How can I cleanly and simply wait for the completion of a process before verifying in JUnit with Mockito?

M. Justin
  • 14,487
  • 7
  • 91
  • 130
  • Is it possible for the method to return a `Future`? – chrylis -cautiouslyoptimistic- Dec 27 '19 at 06:42
  • @chrylis-onstrike- Not really, since it's a fairly orthogonal piece of functionality. In this specific instance, I have a listener listening to changes in the database, and creating an audit record based on the change. So the method that's making the update has no direct knowledge of the audit record being created. – M. Justin Dec 27 '19 at 06:45

1 Answers1

5

This functionality doesn't directly exist in Mockito, as there is currently an open issue to add this functionality to Mockito (Mockito issue #1089).

The solution I am currently using is to write a custom answer for the spied method that waits for the call to be completed before returning. I then verify the result normally afterward.

@SpyBean
private RecordCreationService recordCreationServiceSpy;

@Test(timeout = 10_000)
public void recordShouldBeCreatedWhenDataIsUpdated() {
    // Set up test here

    updateValueAndWait(value);

    assertEquals(1, recordRepository.findAll().size());
    // Perform any additional verifications
}

private void updateValueAndWait(String value) {
    CountDownLatch latch = new CountDownLatch(1);
    Mockito.doAnswer(invocation -> {
        Object result = invocation.callRealMethod();
        latch.countDown();
        return result;
    }).when(recordCreationServiceSpy).insertRecord(any());

    testClass.update(value);

    try {
        latch.await();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}
M. Justin
  • 14,487
  • 7
  • 91
  • 130