测试运行失败:由于'android.os.NetworkOnMainThreadException'

时间:2017-02-22 13:41:24

标签: android android-espresso robotium ui-testing android-instrumentation

我开始使用Espresso UI测试。我为初始化MockTestRunner组件准备了自定义MockApplicationDagger,我也定义了模拟模块。它看起来像是:

public class MockTestRunner extends AndroidJUnitRunner {
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return super.newApplication(cl, MockMyApplication.class.getName(), context);
    } }

MyApp

扩展
public class MockQrApplication extends MyApp {
    private MockWebServer mockWebServer;

    protected void initComponent() {
        mockWebServer = new MockWebServer();

        component = DaggerMyAppComponent
                .builder()
                .myAppModule(new MyAppModule(this))
                .busModule(new BusModule())
                .apiModule(new MockApiModule(mockWebServer))
                .facebookModule(new FacebookModule())
                .dataManagerModule(new DataManagerModule())
                .greenDaoModule(new GreenDaoModule())
                .trackModule(new TrackModule(this))
                .build();

        component.inject(this);
    }
}

我在testInstrumentationRunner

中添加了gradle
defaultConfig {
        ....
        multiDexEnabled true

        testInstrumentationRunner "a.b.c.MockTestRunner"
    }

我希望在LoginActivity

中运行登录测试
@RunWith(AndroidJUnit4.class)
@LargeTest
public class LoginActivityTest {
    protected Solo solo;

    @Rule
    public ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule(LoginActivity.class);

    @Before
    public void setUp() throws Exception {
        initVariables();
    }

    protected void initVariables() {
        solo = new Solo(InstrumentationRegistry.getInstrumentation(), activityTestRule.getActivity());
    }

    @Test
    public void testLayout() {
        solo.waitForFragmentByTag(LoginFragment.TAG, 1000);

        onView(withId(R.id.email_input)).perform(clearText(), typeText("developer@appppp.com"));
        onView(withId(R.id.pass_input)).perform(clearText(), typeText("qqqqqqqq"));
        onView(withId(R.id.login_button)).perform(click());

        solo.waitForDialogToOpen();
    }
}

当我想要运行我的测试时,我得到了:

Client not ready yet..
Started running tests
Test running failed: Instrumentation run failed due to 'android.os.NetworkOnMainThreadException'
Empty test suite.

[编辑]

MockApiModule扩展了ApiModule

public class MockApiModule extends ApiModule {
    private MockWebServer mockWebServer;

    public MockApiModule(MockWebServer mockWebServer) {
        this.mockWebServer = mockWebServer;
    }

    @Override
    public OkHttpClient provideOkHttpClient(DataManager dataManager) {
        return new OkHttpClient.Builder()
                .build();
    }

    @Override
    public Retrofit provideRetrofit(OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(mockWebServer.url("/"))       // throw NetworkOnMainThreadException
                .addConverterFactory(NullOnEmptyConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(new Gson()))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(okHttpClient)
                .build();
        return retrofit;
    }

    @Override
    public ApiService provideApiService(Retrofit retrofit) {
        return retrofit.create(ApiService.class);
    }

    @Override
    public ApiClient provideApiManager(Application application, ApiService apiService, DataManager dataManager) {
        return new MockApiClient(application, apiService, dataManager, mockWebServer);
    }
}

API登录请求如下所示:

apiService.postLoginUser(userLoginModel).enqueue(new Callback<JsonObject>() {
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                if (response.isSuccess()) {
                    LoginResponse loginResponse = null;
                    try {
                        loginResponse = gson.fromJson(MockResponse.getResourceAsString(this, "login.json"), LoginResponse.class);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    callback.onLoginSuccess(loginResponse);
                } else {
                    callback.onLoginFail(response.errorBody().toString());
                }
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                callback.onLoginFail(t.getMessage());
            }
        });

如果我在MockMyApplication

中将MyApp更改为MockTestRunner类应用程序,则此方法有效

我得到stracktrace

android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1285)
at java.net.InetAddress.lookupHostByName(InetAddress.java:431)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
at java.net.InetAddress.getByName(InetAddress.java:305)
at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.java:303)
at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.java:293)
at okhttp3.mockwebserver.MockWebServer.maybeStart(MockWebServer.java:143)
at okhttp3.mockwebserver.MockWebServer.getHostName(MockWebServer.java:172)
at okhttp3.mockwebserver.MockWebServer.url(MockWebServer.java:198)
at com.mooduplabs.qrcontacts.modules.MockApiModule.provideRetrofit(MockApiModule.java:38)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideRetrofitFactory.get(ApiModule_ProvideRetrofitFactory.java:23)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideRetrofitFactory.get(ApiModule_ProvideRetrofitFactory.java:9)
at dagger.internal.ScopedProvider.get(ScopedProvider.java:46)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiServiceFactory.get(ApiModule_ProvideApiServiceFactory.java:23)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiServiceFactory.get(ApiModule_ProvideApiServiceFactory.java:9)
at dagger.internal.ScopedProvider.get(ScopedProvider.java:46)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiManagerFactory.get(ApiModule_ProvideApiManagerFactory.java:31)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiManagerFactory.get(ApiModule_ProvideApiManagerFactory.java:11)
at dagger.internal.ScopedProvider.get(ScopedProvider.java:46)
at com.mooduplabs.qrcontacts.activities.BaseActivity_MembersInjector.injectMembers(BaseActivity_MembersInjector.java:44)
at com.mooduplabs.qrcontacts.activities.BaseActivity_MembersInjector.injectMembers(BaseActivity_MembersInjector.java:13)
at com.mooduplabs.qrcontacts.components.DaggerQrContactsAppComponent.inject(DaggerQrContactsAppComponent.java:91)
at com.mooduplabs.qrcontacts.activities.BaseActivity.init(BaseActivity.java:74)
at com.mooduplabs.qrcontacts.activities.BaseActivity.onCreate(BaseActivity.java:64)
at android.app.Activity.performCreate(Activity.java:6367)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
at android.support.test.runner.MonitoringInstrumentation.callActivityOnCreate(MonitoringInstrumentation.java:532)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2404)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2511)
at android.app.ActivityThread.access$900(ActivityThread.java:165)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1375)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5621)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)

1 个答案:

答案 0 :(得分:0)

遇到类似问题,请避免在测试期间立即开始您的活动。您可以通过调整测试规则来实现此目的,如下面的代码段

@Rule
// Do not launch the activity right away
public ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule(LoginActivity.class, true, false);

然后在测试设置期间自己启动模拟Web服务器。它基本上发生了,因为mockWebServer.url("/")尝试为你启动模拟服务器,以防它尚未启动。如果您不希望稍后在测试执行期间(NetworkOnMainThreadException case)执行此操作,则需要先执行此操作。