Android Espresso测试之前登录一次

34
我一直在尝试使用测试覆盖我的Android应用程序,并最近开始使用espresso。到目前为止,我对它印象深刻。然而,我的应用程序的大部分功能都需要用户登录。由于所有测试都是独立的,因此这需要为每个测试注册一个新用户。这很好用,但由于这个原因,每个测试所需的时间会显著增加。
我正在尝试找到一种方法,在一个测试类中注册一个用户,然后使用同一个用户帐户执行该类中的所有测试。
我已经能够做到这一点的一种方法是实际上只有一个测试(@Test)方法,以我想要的顺序运行所有其他测试。然而,这是一种全面的方法,因为gradle cAT任务仅在最后输出结果,而没有提供可能已经通过/失败的中间测试信息。
我还尝试了@BeforeClass方法,但它没有起作用(即使使用了调试选项,在我使用它的类中也没有gradle输出,似乎在转移到下一个测试类之前花费了很长时间)。

有没有更好的方法在课程开始时注册用户,然后在测试结束时注销?

任何帮助都将不胜感激。


你可以在静态成员变量中注册用户吗? - Ricardo
为什么@BeforeClass不起作用? - Gabriele Mariotti
6个回答

14
理想情况下,您应该在一组只测试不同登录/注销场景的测试中测试登录/注销功能,并让其他测试重点关注其他使用情况。然而,由于其他场景依赖于用户已登录,因此解决这个问题的一种方法似乎是提供一个处理登录的应用程序组件的模拟版本。对于其他需要登录的测试,您将在测试开始时注入此模拟版本,并返回可供整个应用程序使用的模拟用户凭据。
以下是使用Dagger、Mockito和Espresso完成此任务的示例:https://engineering.circle.com/instrumentation-testing-with-dagger-mockito-and-espresso-f07b5f62a85b

9

我测试了一个需要同样场景的应用程序。我解决这个问题最简单的方法是将登录和退出分别拆分成自己的测试类。然后您将所有测试类添加到一个套件中,登录和注销套件分别作为开始和结束。您的测试套件最终看起来像这样。

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

编辑:这里有一个 LoginSetup 和 LogoutTearDown 测试的示例。这个解决方案应该只用于端到端测试,并且占据您测试工作的一小部分。fejd提供了一个完整的测试堆栈的解决方案,也需要考虑。

@LargeTest
public 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());

    }

}

@LargeTest
public 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());

    }

}

能否请您提供 LoginSetup 和 LogoutTearDown 的源代码? - W.K.S
1
当我尝试使用Suite运行设置和拆卸类时,每个类运行完其测试后应用程序就会被关闭。这是否依赖于应用程序缓存已登录的状态? - Kevin Ting

3

使用@Before登陆的方法不错,但如果您的登陆速度很慢,组合测试的时间将非常缓慢。

这里有一个很棒的技巧。策略很简单:按顺序运行每个测试,并在某个测试失败(在本例中是登陆测试)之前,让每个测试都失败。

@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@LargeTest
public 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...

}
优点:
  • 您所要求的功能都能正常工作,并且速度很快(如果您的登录速度很慢)。
  • 您可以进行多个测试,这些测试取决于不同的登录用户,这意味着您可以为user1进行一系列测试,然后为user2进行一系列测试等等。
  • 设置快速。
缺点:
  • 不是标准流程,有人可能会想知道为什么会有那么多测试失败......
  • 您应该模拟用户而不是实际登录。您应该单独测试登录功能,并且测试不应该相互依赖。

3
在你的测试文件中添加以下函数,用你自己的代码替换try块中执行登录操作的代码。
@Before
fun setUp() {
    // Login if it is on the LoginActivity
    try {
        // Type email and password
        Espresso.onView(ViewMatchers.withId(R.id.et_email))
                .perform(ViewActions.typeText("a_test_account_username"), ViewActions.closeSoftKeyboard())
        Espresso.onView(ViewMatchers.withId(R.id.et_password))
                .perform(ViewActions.typeText("a_test_account_password"), ViewActions.closeSoftKeyboard())

        // Click login button
        Espresso.onView(ViewMatchers.withId(R.id.btn_login)).perform(ViewActions.click())
    } catch (e: NoMatchingViewException) {
        //view not displayed logic
    }
}

通过使用 @Before 注解,setUp 函数将在此测试文件中的任何其他测试之前执行。如果应用程序停留在登录活动上,请在此 setUp 函数中进行登录。此示例假定存在用于电子邮件和密码的 EditText,以及一个登录按钮。它使用 Expresso 输入电子邮件和密码,然后点击登录按钮。try catch 块是为了确保如果您没有停留在登录活动上,它将捕获错误并不执行任何操作,如果您没有停留在登录活动上,则可以继续进行其他测试。

注意:这是 Kotlin 代码,但它看起来非常类似于 Java。


2
我的应用程序还要求用户在整个测试运行期间保持登录状态。然而,我能够第一次登录,并且应用程序会记住我的用户名/密码直到我强制让它忘记或重新安装该应用程序为止。
在测试运行期间,每次测试完成后,我的应用程序都会进入后台,并在下一个测试开始时恢复。我猜你的应用程序需要用户每次从后台返回前台时输入他们的凭据(也许是银行应用程序?)。你的应用程序中是否有“记住我的凭据”的设置?如果有,你可以在测试运行中第一次登录后轻松启用它。
除此之外,我认为你应该与开发人员沟通,以提供一种记住你凭据的方式。

1
如果你在Kotlin中使用@BeforeClass,需要将其放置在companion object内。这样,在@BeforeClass下方的代码块将在类中第一个测试运行之前仅执行一次。同样,在companion object内放置@AfterClass,以便在类的最后一个测试运行结束时执行它。
请记住,一旦编译器从companion object内部移动到外部,应用程序下的上下文就会丢失。您可以通过启动应用程序的主活动(登录后的活动)来恢复上下文。
 companion object {
        @ClassRule
        @JvmField
        val activity = ActivityTestRule(Activity::class.java)

        @BeforeClass
        @JvmStatic
        fun setUp() {
            // login block
        }

        @AfterClass
        @JvmStatic
        fun tearDown() {
            // logout block
        }
    }

    @Test
    fun sampleTest() {
        activity.launchActivity(Intent())
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接