android espresso login once before running tests android espresso login once before running tests android android

android espresso login once before running tests


Ideally you would test the login/logout functionality in a set of tests that just test different login/logout scenarios, and let the other tests focus on other use cases. However, since the other scenarios depend on the user being logged in, it sounds like one way to solve this would be to provide a mock version of the app component handling the login. For the other login dependent tests, you would inject this mock at the start and it would return mock user credentials that the rest of the app can work with.

Here's an example where Dagger, Mockito and Espresso is being used to accomplish this: https://engineering.circle.com/instrumentation-testing-with-dagger-mockito-and-espresso-f07b5f62a85b


I test an app that requires this same scenario. The easiest way I've gotten around this is to split up logging in and out into their own test classes. Then you add all your test classes to a suite, starting and ending with the login and logout suites respectively. Your test suites ends up looking kind of like this.

@RunWith(Suite.class)@Suite.SuiteClasses({        LoginSetup.class,        SmokeTests.class,        LogoutTearDown.class})

EDIT: Here is an example of both the LoginSetup and LogoutTearDown tests. This solution really should only be for end-to-end tests and comprise a small portion of your testing efforts. fejd provides a solution for a full testing stack which also needs to be considered.

@LargeTestpublic class SmokeSetup extends LogInTestFixture {    @Rule    public ActivityTestRule<LoginActivity> mLoginActivity = new ActivityTestRule<>(LoginActivity.class);    @Test    public void testSetup() throws IOException {        onView(withId(R.id.username_field)).perform(replaceText("username"));        onView(withId(R.id.password_field)).perform(replaceText("password"));        onView(withId(R.id.login_button)).perform(click());    }}@LargeTestpublic class LogoutTearDown extends LogInTestFixture {    @Rule    public ActivityTestRule<MainActivity> mMainActivity = new ActivityTestRule<>(MainActivity.class);    @Test    public void testLogout() throws IOException {        onView(withId(R.id.toolbar_menu)).perform(click());        onView(withId(R.id.logout_button)).perform(click());    }}


The approach with logging in with @Before is nice but if your login is slow, your combined test time will be very slow.

Here's a great hack that works. The strategy is simple: run every test in order and fail every test before they get a chance to run if a certain test fails (in this case login test).

@RunWith(AndroidJUnit4.class)@FixMethodOrder(MethodSorters.NAME_ASCENDING)@LargeTestpublic class YourTestsThatDependsOnLogin {    private static failEverything;    @Before    public void beforeTest() {        // Fail every test before it has a chance to run if login failed        if (failEverything) {            Assert.fail("Login failed so every test should fail");        }    }        @Test    public void test0_REQUIREDTEST_login() {        failEverything = true;        // Your code for login        // Your login method must fail the test if it fails.        login();        failEverything = false; // We are safe to continue.    }    // test1 test2 test3 etc...}

Pros:

  • What you asked for works and it is fast (if your login is slow)
  • You can have multiple tests that depend on different logins, meaning you can do a bunch of tests for user1, then a bunch of tests for user2 etc.
  • Quick to set up.

Cons:

  • Not standard procedure and someone might wonder why so many testsfail...
  • You should mock users instead of actually logging in. You should test your login separately and tests should not depend on each other.