Android 在使用 junit 进行测试期间处理程序为空

Android Handler is null during testing with junit

我正在尝试测试我的网络模块。当我 运行 在模拟器或设备上执行此操作时,处理程序正常,但是当我尝试通过测试执行此操作时,处理程序 = null 并且不会调用回调。我该如何解决这个问题?

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);
                    }
                }
            });
        }
    });
}

我的 gradle 应用程序文件包含此参数。

testOptions {
    unitTests.returnDefaultValues = true
}

如果您 运行 JUnit 上的此示例代码,这将不起作用,因为 JUnit 测试 运行 在 JVM 上进行,而 Instrumented 测试 运行 在模拟器上进行或真实设备

你可以看看这个link,它解释了原因:

Instrumented tests or Local tests

好的,经过几个小时的研究,我找到了解决方案,它类似于 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();

}