Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get new instance of the my Application class per each unit test?

I have an Android app that has MyApplication class that inherits from Application.

I've created few unit tests that run with @RunWith(AndroidJUnit4.class). If I run each test separately they all pass. If I run them together - first one passes and then (some of the) others fail.

The problem is that it seems that only one instance of the MyApplication is created and then it is preserved and used for all test which causes the fails because there is a state in the MyApplication which MUST be initialized only once.

Is there a way to run the unit tests (androidTest) so application is restarted for each test? I don't care if it will be slow (e.g. app will have to be reinstalled each time) I just want tests to run independently of one another.

Actual code from the unit test looks like (as requested by @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);
    }
}
like image 819
Ognyan Avatar asked Oct 12 '16 08:10

Ognyan


People also ask

Does JUnit creates a new instance for each test?

JUnit creates a new instance of the test class before invoking each @Test method. This helps provide independence between test methods and avoids unintentional side effects in the test code. Because each test method runs on a new test class instance, we can't reuse instance variable values across test methods.

How do you unit test your application?

Unit tests can be performed manually or automated. Those employing a manual method may have an instinctual document made detailing each step in the process; however, automated testing is the more common method to unit tests. Automated approaches commonly use a testing framework to develop test cases.

How do you create a unit test class?

To get started, select a method, a type, or a namespace in the code editor in the project you want to test, right-click, and then choose Create Unit Tests. The Create Unit Tests dialog opens where you can configure how you want the tests to be created.


1 Answers

The problem is that it seems that only one instance of the MyApplication is created and then it is preserved and used for all test which causes the fails because there is a state in the MyApplication which MUST be initialized only once.

IMHO, that's a bug in the app, which should be fixed. Application is an unsuitable location for much true business logic (though it is fine for initializing your crash-reporter library, StrictMode, etc.). Everything else should be testable individually, either directly, via mocks, via dependency injection, etc.

That being said, sometimes the problem isn't in code that you control, but rather code from libraries or the framework.

Is there a way to run the unit tests (androidTest) so application is restarted for each test?

Nowadays, yes, though there wasn't at the time this question was asked. The Android Test Orchestrator (ATO) does a better job of isolating each test, at a cost of test execution speed.

like image 188
CommonsWare Avatar answered Sep 20 '22 15:09

CommonsWare