Monday 13 May 2019

Test callback logic

Suppose we need to write unit tests for the Service class. The Dao class interacts with database, so it needs to be mocked.

class Service{
    
    Dao dao;
    
    Service(Dao dao){
        this.dao = dao;
    }
    
    public void doStuff(String s) {
        Callback<String> callback = i -> {
            System.out.println("Hello "+i);  
        };
        dao.doStuff(s, callback);
    }
    
}

class Dao{
    public void doStuff(String s, Callback<String> callback) {
        callback.apply(s);
        //Some DB operation
    }
    
}

interface Callback<T>{
    void apply(T t);
}

The question is how to test the callback logic (the System.out.println() line). If we simply write test case as below, it doesn't print out 'Hello John'.



@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
public class CallbackTest {

    @Mock Dao dao;
    
    @Test
    void test() {
        Service service = new Service(dao);
        service.doStuff("John");
    }

}

Well, we can mock dao.doStuff() by using doAnswer() method. It allows us to define the method body. What we do here is only invoking callback.apply(s) while skipping the database operations.


@Test
void test() {
    Service service = new Service(dao);
    doAnswer(i -> {
        Callback<String> callback = i.getArgument(1);
        callback.apply(i.getArgument(0));
        return null;
    }).when(dao).doStuff(anyString(), any());
    service.doStuff("John");
}