Search code examples
androidunit-testingretrofitandroid-mvp

Unittesting the cancellation of a retrofit call


I'm trying to write a unittest that tests a cancellation of a Retrofit call, but I'm stuck.

Here's the implementation:

public class LoginInteractorImpl extends AbstractInteractor implements LoginInteractor {

    private volatile Call<ResponseBody> mCall;

    private UserRepo mUserRepository;

    @Inject
    public LoginInteractorImpl(WorkerThread workerThread, MainThread mainThread,  UserRepo userRepository) {
        super(workerThread,mainThread);
        this.mUserRepository = userRepository;
    }

    @Override
    public void login(final String username, final String password, final OnLoginFinishedListener listener) {
    mWorkerThread.execute(new Runnable() {
            @Override
            public void run() {
                mCall = mUserRepository.doLogin(Credentials.basic(username, password));

                enqueue(new LoginCallbackImpl(mWorkerThread, mMainThread, listener));
            }
        });

    }

    @Override
    public void cancel() {
        if(mCall != null) {
            mCall.cancel();
        }
    }

    void enqueue(LoginCallback callback){
        mCall.enqueue(callback);
    }
}

Here's what I've got so far in my testclass:

public class LoginInteractorTest {

    private LoginInteractorImpl mLoginInteractor;

    @Mock
    UserRepo mockUserRepository;

    @Mock
    private Call<ResponseBody> mockCall;

    @Mock
    private LoginInteractor.OnLoginFinishedListener mockLoginListener;

    @Before
    public void setUp() throws Exception {

        MockitoAnnotations.initMocks(this);

        mLoginInteractor = new LoginInteractorImpl(new FakeWorkerThread(), new FakeMainThread(), mockUserRepository);
    }

    ...

    @Test
    public void shouldCancelLoginCall() throws Exception {

        final String username = "test";
        final String password = "testtest";

        LoginInteractorImpl spy = Mockito.spy(mLoginInteractor);

        when(mockUserRepository.doLogin(anyString()))
            .thenReturn(mockCall);

        final CountDownLatch latch = new CountDownLatch(1);

        // Somehow delay the call and cancel it instead?

        when(mLoginInteractor.enqueue(any(LoginCallback.class)))
            .thenAnswer(
                // Somehow pospone the call from executing
            );

        // Enqueue logincall
        mLoginInteractor.login(username, password, mockLoginListener);

        // Cancel call
        mLoginInteractor.cancel();

        // Verify that cancel was called
        verify(mockCall, times(1)).cancel();
    }
}

My question is how can I stop the mockCall from being executed and verify that I've succeeded in doing my cancelation? My best bet is that I somehow have to use CountDownLatch, but I've never used it before and haven't been able to find and answer anywhere on how to use it in my usecase.


Solution

  • Found the answer myself:

    public void shouldCancelLoginCall() throws Exception {
    
        final String username = "test";
        final String password = "testtest";
    
        LoginInteractorImpl spy = Mockito.spy(mLoginInteractor);
    
        when(mockUserRepository.doLogin(anyString())).thenReturn(mockCall);
    
        doNothing().when(spy).enqueue(any(LoginCallback.class));
    
        mLoginInteractor.login(username, password, mockLoginListener);
    
        mLoginInteractor.cancel();
    
        verify(mockCall, times(1)).cancel();
    }