如何在每个单元测试中获取我的Application类的新实例?

时间:2016-10-12 08:00:01

标签: android unit-testing

我有一个Android应用,其MyApplication类继承自Application

我创建了几个与@RunWith(AndroidJUnit4.class)一起运行的单元测试。如果我单独运行每个测试,他们都会通过。如果我一起运行 - 第一个通过然后(其中一些)其他人失败。

问题是,似乎只创建了MyApplication的一个实例,然后将其保留并用于导致失败的所有测试,因为MyApplication中有一个状态必须只被初始化一次。

有没有办法运行单元测试(androidTest),以便为每个测试重新启动应用程序?我不在乎它是否会很慢(例如每次都必须重新安装应用程序)我只是想让测试彼此独立运行。

单元测试的实际代码如下(根据@Zinc的要求):

@RunWith(AndroidJUnit4.class)
public class AutoLogin_ActMainTest {
    @Rule
    public ActivityTestRule<ActMain> mActivityRule = new ActivityTestRule<ActMain>(
            ActMain.class) {


        @Override
        protected void beforeActivityLaunched() {
            super.beforeActivityLaunched();

            MyTestApp app = (MyTestApp) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
            DependencyInjector.reset();
            app.reset();


            FakeUnitDaggerModule fudm = new FakeUnitDaggerModule();

            Session session = new SessionImpl(new TimeProviderImpl());
            fudm.setResMain(new ResMainTest(session));

            FakeAppPrefs appPrefs = new FakeAppPrefs();
            FakeLoginPrefs loginPrefs = new FakeLoginPrefs();
            CurrentUserHolder currentUserHolder = new CurrentUserHolder();

            FakeComponent inj = DaggerFakeComponent.builder().
                    fakeMyAppDaggerModule(new FakeMyAppDaggerModule(app, appPrefs, loginPrefs, currentUserHolder)).
                    appInfoDaggerModule(new AppInfoDaggerModule("1")).
                    fakeSessionDaggerModule(new FakeSessionDaggerModule(session)).
                    fakeExchangeDaggerModule(new FakeExchangeDaggerModule("https://test.com")).
                    fakeUnitDaggerModule(fudm).
                    build();

            DependencyInjector.init(inj);
            DependencyInjector.getInstance().inject(app);


            app.onStart();
        }
    };


    @Test
    public void testAutoLogin() {
        ElapsedTimeIdlingResource idlingResource = new ElapsedTimeIdlingResource(500);
        Espresso.registerIdlingResources(idlingResource);
        idlingResource.startWaiting();

        onView(ViewMatchers.withId(R.id.tv_logged_in_as)).check(matches(isDisplayed()));
        Espresso.unregisterIdlingResources(idlingResource);
    }
}

4 个答案:

答案 0 :(得分:4)

  

问题在于,似乎只创建了一个MyApplication实例,然后将其保留并用于导致失败的所有测试,因为MyApplication中的状态必须只初始化一次。

恕我直言,这是应用程序中的一个错误,应该修复。 Application对于许多真正的业务逻辑来说是一个不合适的位置(尽管它可以初始化崩溃报告库,StrictMode等)。其他所有东西都应该单独测试,可以直接测试,通过模拟,依赖注入等等。

话虽如此,有时问题不是你控制的代码,而是来自库或框架的代码。

  

有没有办法运行单元测试(androidTest),以便每次测试重启应用程序?

现在,是的,虽然当时没有问过这个问题。 Android Test Orchestrator(ATO)可以更好地隔离每个测试,但代价是测试执行速度。

答案 1 :(得分:2)

您需要重构您的应用程序,以便控制状态的任何代码都不会绑定到应用程序类,而是绑定到另一个对象中。然后,您可以重置该部分或模拟它,而无需关心应用程序类的持久性。优选地,这是使用一些形式依赖性注入来完成的。

答案 2 :(得分:0)

我对你的问题不太确定。但您可以使用ApplicationTestCase。有点像:

public class MyApplicationTest extends ApplicationTestCase<MyTestApp> {
    public void test1() {
        createApplication();

        ... test here ...

        terminateApplication();
    }

    public void test2() {
        createApplication();

        ... test here ...

        terminateApplication();
    }
}

参考:https://developer.android.com/reference/android/test/ApplicationTestCase.html

答案 3 :(得分:0)

    public class TestApplication extends Application {

@Override
public void onCreate() {
    super.onCreate();
    // Sdk.terminate(); - If you specify TestApplication as an 
    //                    application class in AndroidManifest, 
    //                    you'll have to uncomment this(due to issue with test runner)
    Sdk.initialize();
}

@Override
public void onTerminate() {
    super.onTerminate();
    Sdk.terminate();
}
}

Sdk课程

    public class Sdk {

private static Sdk sInstance;
private void Sdk(){
}

public static Sdk getInstance() throws RuntimeException {
    if (sInstance == null) {
        throw new RuntimeException();
    }
    return sInstance;
}

public static void terminate() {
    sInstance = null;
}

public static void initialize() {
    if (sInstance == null) {
        sInstance = new Sdk();
        //save some information according to what is on the default configurations
    } else {
        throw new RuntimeException("Method was already initialized");
    }
}}

试验:

    public class MyApplicationTest extends ApplicationTestCase<TestApplication> {

public MyApplicationTest() {
    super(TestApplication.class);
}

public void testMultiplicationTests() {
    createApplication();

    int answer = 42;
    assertEquals(42, answer);

    terminateApplication();
}


public void testDefaultSettings() {
    createApplication();

    assertNotNull(Sdk.getInstance());

    terminateApplication();
}}