33

I have a method like the following,

public void generateCSVFile(final Date billingDate) {
    asyncTaskExecutor.execute(new Runnable() {
        public void run() {
            try {
                accessService.generateCSVFile(billingDate);
            } catch (Exception e) {
                LOG.error(e.getMessage());
            }
        }
    });
}

I have mocked:

PowerMockito.doNothing().when(accessService).generateCSVFile(billingDate);

But when I verify:

verify(rbmPublicViewAccessService, timeout(100).times(1)).generateCSVFile(billingDate);

It gives me as not invoked. Is this because it is invoked via separate thread, and is it possible to verify the methods called in different thread?

Ahmad Hosny
  • 597
  • 1
  • 6
  • 23
kuhajeyan
  • 10,727
  • 10
  • 46
  • 71

4 Answers4

46

It is very likely that the Runnable hasn't been executed yet by the asyncTaskExecutor when you verify the invocation, resulting in a verification error in your unit test.

The best way to fix this is to join on the generated thread and wait for execution before verifying the invocations.

If you cannot get the instance of the thread, a possible work around is to mock the asyncTaskExecutor and implement it so it will execute the runnable directly.

private ExecutorService executor;

@Before
public void setup() {
    executor = mock(ExecutorService.class);
    implementAsDirectExecutor(executor);
}

protected void implementAsDirectExecutor(ExecutorService executor) {
    doAnswer(new Answer<Object>() {
        public Object answer(InvocationOnMock invocation) throws Exception {
            ((Runnable) invocation.getArguments()[0]).run();
            return null;
        }
    }).when(executor).submit(any(Runnable.class));
}
Jing Li
  • 14,547
  • 7
  • 57
  • 69
Tom Verelst
  • 15,324
  • 2
  • 30
  • 40
21

I had the same issue and played around with the timeout argument http://javadoc.io/page/org.mockito/mockito-core/latest/org/mockito/Mockito.html#22 but with argument 0 like in

verify(someClass, timeout(0)).someMethod(any(someParameter.class));

And it works. I assume that the testing thread yields, and therefore the other thread has an opportunity to do its work, calling the mocks appropriately. Still it smells like a hack.

Yaroslav Stavnichiy
  • 20,738
  • 6
  • 52
  • 55
Francis Martens
  • 3,458
  • 4
  • 24
  • 26
3

To further iterate upon Tom's answer - using Java 8 Lambdas you can now use the following code for mocking the Executor, which is slightly more concise:

    doAnswer((Answer<Void>)invocation -> {
        ((Runnable)invocation.getArgument(0)).run();
        return null;
    }).when(executorService).submit(any(Runnable.class));
RobinFood
  • 156
  • 1
  • 7
1

It took me hours to figure what is going on! I simply added Thread.sleep after calling the method under test and before calling verify. This will guarantee that the created threads finish before calling verify method.

@Test
void testVerifyWorksForCodeRunsInThreads() {
    //given: 

    //when:
    //call the method under test which runs threads
    Thread.sleep(200);//MUST HAVE THIS BEFORE CALLING VERIFY TO MAKE SURE THE CREATED THREADS FINISH. Set the time based on how long your task would take

    //then: 
    verify(.......
}
Mosheer
  • 274
  • 2
  • 5