Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito with Java async->sync converter

I have an async method I am converting to a sync method using a countdown latch. I'm struggling with writing a unit test without using mockito's timeout function. I can't work out how to get the verify method to wait for the async method call:

public interface SyncExchangeService {
    boolean placeOrder(Order order);
}
public interface ExchangeService {
    void placeOrder(Order order, OrderCallback orderResponseCallback);
}

public interface OrderCallback {
    public void onSuccess();
    public void onFailure();
}



public class SyncExchangeServiceAdapter implements SyncExchangeService {
    private ExchangeService exchangeService;

    public SyncExchangeServiceAdapter(ExchangeService exchangeService) {
        this.exchangeService = exchangeService;
    }

    @Override
    public boolean placeOrder(Order order) {

        final CountDownLatch countdownLatch=new CountDownLatch(1);
        final AtomicBoolean result=new AtomicBoolean();
        exchangeService.placeOrder(order, new OrderCallback() {

            @Override
            public void onSuccess() {
                result.set(true);
                countdownLatch.countDown();
            }

            @Override
            public void onFailure(String rejectReason) {
                result.set(false);
                countdownLatch.countDown();
            }
        });
        try {
            countdownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return result.get();
    }
}


public class SyncExchangeServiceAdapterTest {
    private ExchangeService mockExchange=mock(ExchangeService.class);
    private SyncExchangeServiceAdapter adapter=new SyncExchangeServiceAdapter(mockExchange);
    private Boolean response;
    private ArgumentCaptor<Boolean> callback=CaptorArgumentCaptor.forClass(OrderCallback.class);
    private CountDownLatch latch=new CountDownLatch(1);


    @Test
    public void testPlaceOrderWithSuccess() throws Exception {
        final Order order=mock(Order.class);
         Executors.newSingleThreadExecutor().submit(new Runnable() {
            @Override
            public void run() {
                response=adapter.placeOrder(order);
                latch.countDown();
            }
        });
            verify(mockExchange,timeout(10) ).placeOrder(eq(order), callbackCaptor.capture());
//the timeout method is not really recommended and could also fail randomly if the thread takes more than 10ms


        callbackCaptor.getValue().onSuccess();
        latch.await(1000,TimeUnit.MILLISECONDS);
            assertEquals(true,response);
    }


}
like image 544
DD. Avatar asked Jun 19 '12 13:06

DD.


1 Answers

For these kinds of tests I like to use a little library called awaitility. You can do it yourself with a countdown latch, but as you have seen you have to hack up your test with a machete to make that work.

In this test you should call verify after you have awaited the latch.

Another problem in your code is the private Boolean response. Since you're changing it in another thread you should make that an AtomicBoolean or at least declare it volatile.

like image 187
iwein Avatar answered Oct 14 '22 06:10

iwein