npmrtsv
npmrtsv

Reputation: 434

Android Handler is null during testing with junit

I'm trying to test my network module. When I run this on simulator or device, handler is ok, but when I'm trying to do it from tests, handler = null and callback doesn't get called. How can I solve this problem?

public void performCall(Call callToPerform){
    callToPerform.call.enqueue(new okhttp3.Callback() {
        Handler handler = new Handler();

        @Override
        public void onFailure(okhttp3.Call call, IOException e) {
            handler.post(() -> {
                for (Callback callback : callToPerform.callbacks) {
                    callback.onFailure(callToPerform, e);
                }
            });
        }

        @Override
        public void onResponse(okhttp3.Call call, final okhttp3.Response response){
            handler.post(() -> {
                for (Callback callback : callToPerform.callbacks) {
                    try {
                        callback.onResponse(callToPerform, new Response(response.body().bytes(), response.headers().toMultimap()));
                    } catch (IOException e) {
                        callback.onFailure(call, e);
                    }
                }
            });
        }
    });
}

My graddle app file contains this params.

testOptions {
    unitTests.returnDefaultValues = true
}

Upvotes: 3

Views: 3940

Answers (2)

npmrtsv
npmrtsv

Reputation: 434

Ok, after a few hours of research I've found solution and it's similar to this:

package com.dpmedeiros.androidtestsupportlibrary;

import android.os.Handler;
import android.os.Looper;

import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static org.mockito.Mockito.*;

/**
 * Utility methods that unit tests can use to do common android library mocking that might be needed.
 */
public class AndroidMockUtil {

    private AndroidMockUtil() {}
    /**
     * Mocks main thread handler post() and postDelayed() for use in Android unit tests
     *
     * To use this:
     * <ol>
     *     <li>Call this method in an {@literal @}Before method of your test.</li>
     *     <li>Place Looper.class in the {@literal @}PrepareForTest annotation before your test class.</li>
     *     <li>any class under test that needs to call {@code new Handler(Looper.getMainLooper())} should be placed
     *     in the {@literal @}PrepareForTest annotation as well.</li>
     * </ol>
     *
     * @throws Exception
     */
    public static void mockMainThreadHandler() throws Exception {
        PowerMockito.mockStatic(Looper.class);
        Looper mockMainThreadLooper = mock(Looper.class);
        when(Looper.getMainLooper()).thenReturn(mockMainThreadLooper);
        Handler mockMainThreadHandler = mock(Handler.class);
        Answer<Boolean> handlerPostAnswer = new Answer<Boolean>() {
            @Override
            public Boolean answer(InvocationOnMock invocation) throws Throwable {
                Runnable runnable = invocation.getArgumentAt(0, Runnable.class);
                Long delay = 0L;
                if (invocation.getArguments().length > 1) {
                    delay = invocation.getArgumentAt(1, Long.class);
                }
                if (runnable != null) {
                    mainThread.schedule(runnable, delay, TimeUnit.MILLISECONDS);
                }
                return true;
            }
        };
        doAnswer(handlerPostAnswer).when(mockMainThreadHandler).post(any(Runnable.class));
        doAnswer(handlerPostAnswer).when(mockMainThreadHandler).postDelayed(any(Runnable.class), anyLong());
        PowerMockito.whenNew(Handler.class).withArguments(mockMainThreadLooper).thenReturn(mockMainThreadHandler);
    }

    private final static ScheduledExecutorService mainThread = Executors.newSingleThreadScheduledExecutor();

}

Upvotes: 3

Maxime Liege
Maxime Liege

Reputation: 120

If you run this sample code on JUnit, this will not work because JUnit tests are running on a JVM, and Instrumented tests are running on a Simulator or Real Device

You can take a look at this link, it explains why :

Instrumented tests or Local tests

Upvotes: 2

Related Questions