Search code examples
javaunit-testingconcurrencycompletable-future

Unittest verify async method Java


I want to verify that when run methodA, my code run through methodB asynchronously (1 time) like below

public class ClassA {

    @Inject
    @Named("executor")
    ExecutionContextExecutor eventHandlerExecutor;

    public Object methodA (Object x, Object y) {
        ...
        CompletableFuture.runAsync(() -> methodB(i, j), eventHandlerExecutor);
        ...
    }

    public void methodB (Object n, Object m) {
        ...
    }
}

How should I write a unit test for this?

classA.methodA(x, y);

// can spying classA help to verify it?
verify(classA, times(1)).methodB(any(), any());

Solution

  • Yes. You can spy the ClassA instance you want to test. The nice thing of a spy is that it allows you to test the real thing but at the same time can also let you to verify if certain of its method are really executed or not.

    Another nice thing is that it allow you to add some codes that will be executed before actually executing the real method by using doAnswer(). So you can make use of it to capture the thread name that is used to execute methodB for verification later , and also setup a CountDownLatch such that the main thread will wait for the aysnc. thread to finish before proceeding.

    So something likes as below after combining all of these :

    @Test
    void test(){
        ClassA sut = new ClassA(xxx);
        sut = spy(sut);
    
        CountDownLatch latch = new CountDownLatch(1);
        AtomicReference<Thread> asyncThreadRef = new AtomicReference();
    
         doAnswer(ans -> {
                try {
                   asyncThreadRef.set(Thread.currentThread());
                    return ans.callRealMethod();
                } finally {
                    latch.countDown();
                }
         }).when(sut).methodB(eq(aaa), eq(bbb));
        
        sut.methodA(aaa,bbb);
        latch.await(500, TimeUnit.MILLISECONDS);
    
        verify(sut, times(1)).methodB(aaa, bbb);
        assertThat(asyncThreadRef.get()).isNotSameAs(Thread.currentThread());   
    }