Android Test - 使用 Espresso 验证特殊情况-界面的快速跳转

Android Test - 使用 Espresso 验证特殊情况-界面的快速跳转,第1张

涵盖单个应用的界面测试:这种类型的测试可验证目标应用在用户执行特定 *** 作或在其 Activity 中输入特定内容时的行为是否符合预期。

Espresso 会检测主线程何时处于空闲状态,以便可以在适当的时间运行测试命令,从而提高测试的可靠性

最近遇到这样的测试示例场景:
项目使用的是 Navigation (导航组件),点击 FragmentA 的按钮,导航到 FragmentB,FragmentB的 onViewCreated() 中有个方法,访问Api,如果Api有返回值,直接导航到 FragmentC,反之导航到 FragmentD。

很简单的逻辑:测试示例代码:

@LargeTest
@RunWith(AndroidJUnit4::class)
class SetupTests {

    companion object {
        const val SETUP_CONNECTING = "com.***.***.ui.setup.FragmentB"
    }

    @get:Rule
    val activityScenarioRule = ActivityScenarioRule(Main::class.java)

    @Test
    fun testSetupConnectingWithIssue2() {
        onView(ViewMatchers.withText("FragmentA's Text")).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
        onView(ViewMatchers.withId(R.id.buttonAToB)).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
            .perform(ViewActions.click())
        onView(ViewMatchers.withText("FragmentB's Text")).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
        // 等待Api30秒的超时时间,该方法是自己封装的,下面有具体实现链接,感兴趣的可以去看下
        IntegrationUtil.waitForElementToAppear(
            onView(ViewMatchers.withText("FragmentC's Text")),
            30
        )
    }
}

运行上面的test,发现存在问题:
⚠️ 如果网络状况一般,该测试可以运行通过;但是网络状况很好,可以知道的是 FragmentB,存在的时间极短,一闪而过。

onView(ViewMatchers.withText("FragmentB's Text")).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))

分析:该验证 FragmentB的代码就会报错,提示找不到该元素,因为Espresso 会检测主线程何时处于空闲状态,等主线程有空闲的时候,才会执行该验证,此时的 FragmentB 已经跳转到FragmentC了。也就是说,这里如果继续使用Espresso来校验UI是不能成功的。

解决思路:测试 fragment 导航,类似如下代码:

@RunWith(AndroidJUnit4::class)
class TitleScreenTest {

    @Test
    fun testNavigationToInGameScreen() {
        // Create a TestNavHostController
        val navController = TestNavHostController(
            ApplicationProvider.getApplicationContext())

        // Create a graphical FragmentScenario for the TitleScreen
        val titleScenario = launchFragmentInContainer()

        titleScenario.onFragment { fragment ->
            // Set the graph on the TestNavHostController
            navController.setGraph(R.navigation.trivia)

            // Make the NavController available via the findNavController() APIs
            Navigation.setViewNavController(fragment.requireView(), navController)
        }

        // Verify that performing a click changes the NavController’s state
        onView(ViewMatchers.withId(R.id.play_btn)).perform(ViewActions.click())
        assertThat(navController.currentDestination?.id).isEqualTo(R.id.in_game)
    }
}

使用 比较 currentDestination来验证FragmentB界面,但是我运行后,发现代码: navController.setGraph(R.navigation.trivia) 一直报错,而我也没能解决这个原因,就另辟蹊径;

如下代码:

@LargeTest
@RunWith(AndroidJUnit4::class)
class SetupTests {

    companion object {
        const val FRAGMENT_B = "com.***.***.ui.setup.FragmentB"
    }

    private var navController: NavController? = null

    @get:Rule
    val activityScenarioRule = ActivityScenarioRule(Main::class.java)

    @Before
    fun beforeTests() {
        activityScenarioRule.scenario.onActivity {
            navController = it.findNavController(R.id.nav_host)
        }
    }

    @Test
    fun testSetupConnectingWithIssue2() {
        onView(ViewMatchers.withText("FragmentA's Text")).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
        onView(ViewMatchers.withId(R.id.buttonAToB)).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
            .perform(ViewActions.click())
        IntegrationUtil.waitForElementToAppear(
            onView(ViewMatchers.withText("FragmentC's Text")),
            30
        )
        val hasFragmentBDisplayed =
            (navController?.previousBackStackEntry?.destination as? FragmentNavigator.Destination)?.className == FRAGMENT_B
        Assert.assertTrue("verify ‘FragmentB’ screen has displayed", hasFragmentBDisplayed)
        Thread.sleep(1000)
    }
}

因为FragmentB显示时长的不确定性,不能直接去验证,包括使用currentDestination也是不行的,所以等FragmentC界面验证通过后,我们在验证 previousBackStackEntry 是不是 FragmentB,如果是,则经过FragmentB界面,反之则没有经过FragmentB。

补充:

1. Espresso具有同步功能:
每次测试调用 onView() 时,Espresso 都会等待执行相应的界面 *** 作或断言,直到满足以下同步条件:

  1. 消息队列为空。
  2. 没有当前正在执行任务的 AsyncTask 实例。
  3. 开发者定义的所有空闲资源都处于空闲状态。

通过执行这些检查,Espresso 大大提高了在任何给定时间只能发生一项界面 *** 作或断言的可能性。此功能可给您带来更可靠的测试结果。

2. waitForElementToAppear() 是自己封装的校验UI,

有兴趣的可以查看下一篇文章:Android Test - Espresso 延迟匹配的实现

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/906174.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-15
下一篇 2022-05-15

发表评论

登录后才能评论

评论列表(0条)

保存