多个匹配语句时 Espresso 测试失败,但使用较小的匹配语句通过

Espresso test fails when multiple match statements but passes with smaller match statements

我有一个 Instrument 测试,其中有四个匹配语句。四个语句中有一个在 运行 与其他三个语句一起时失败,但如果单独 运行 则通过。

这是失败的测试

@Test
fun displayTypeFilteredPokemon(){
    // When - PokemonList fragment launch and filtered by specific type
    launchActivity()
    onView(withId(R.id.genAllButton)).perform(click())


    // Perform click action to do the filter on specific type
    onView(withId(R.id.menu_filter)).perform(click())
    onView(withText(R.string.label_type_electric)).perform(click())

    // Then - Verify the list is filtered by the selected type
    onView(withId(R.id.pokemon_list)).check(RecyclerViewItemCountAssertion(3))
    onView(withText("Magnemite")).check(matches(isDisplayed()))
    onView(withText("Jolteon")).check(matches(isDisplayed()))
    onView(withText("Emolga")).check(matches(isDisplayed()))
}

这里是启动代码activity:

private fun launchActivity(): ActivityScenario<PokemonListActivity>? {
    val scenario = launch(PokemonListActivity::class.java)

    scenario.onActivity {
        val intent = Intent()
        intent.putExtra("genId",0)
        it.intent = intent
    }

    return scenario
}

这里是自定义匹配器代码:

class RecyclerViewItemCountAssertion(private val matcher: Int) : ViewAssertion {

override fun check(view: View?, noViewFoundException: NoMatchingViewException?) {
    if(noViewFoundException != null){
        throw noViewFoundException
    }

    val recyclerView = view as RecyclerView
    val adapter = recyclerView.adapter!!

    assertThat(adapter.itemCount, IsEqual(matcher))
}

}

当使用这组匹配时,它通过了:

onView(withId(R.id.pokemon_list)).check(RecyclerViewItemCountAssertion(3))
onView(withText("Jolteon")).check(matches(isDisplayed()))
onView(withText("Emolga")).check(matches(isDisplayed()))

或者当这组匹配也通过时:

onView(withText("Magnemite")).check(matches(isDisplayed()))

这是正在测试的视图:

我有点困惑,因为视图中显然有匹配的文本。可能是资源没有空闲,因此测试刚刚结束?例如,只有一个匹配器的测试通过的原因是速度足够快,可以在完成之前进行匹配?

我考虑过引入 EspressoIdlingResource,但我读到这会在代码库中引入困难,为了学习目的,我希望保持简单。

我认为竞争条件是一个问题的另一个指标是,当我调试测试时它通过了。当我 运行 测试失败时。

编辑 1: 当 运行 自己进行所有测试时,我没有错误。当 运行 对 class 中的所有测试进行测试时,出现以下错误:

androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: com.stegner.androiddex:id/menu_filter

您编写浓缩咖啡测试的方法似乎是错误的。

在编写UI测试时,我们要确认的是以下内容。 "If user interacts clicks button X, swipes the page, writes 500 to this edittext, and clicks button Y, he will be able to see text Z"。 如您所见,我们正在测试应用程序,就好像我们是最终用户一样,并且只是与我们在屏幕上看到的元素进行交互。

private fun launchActivity(): ActivityScenario<PokemonListActivity>? {
    val scenario = launch(PokemonListActivity::class.java)

    scenario.onActivity {
        val intent = Intent()
        intent.putExtra("genId",0)
        it.intent = intent
    }

    return scenario
}

当您使用 launchActivity 并通过意图传递数据时,您无视我们要测试的内容,最终用户无法通过意图传递数据,他们只需点击一个按钮即可触发我们的代码以意图传递数据。所以一个好的测试,应该从头到尾(从启动屏幕到你想要的屏幕)模仿用户的动作(点击 X,点击 Y,写 500 到 Z 等) 现在您的问题很可能是由于在方法内的每个测试中都使用 launchActivity 造成的,第二个测试无法启动 Activity (至少它无法启动 activity 足够快)所以你在第一个断言中出错。

androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: com.stegner.androiddex:id/menu_filter

您应该从您的测试方法中删除 launchActivity() 并使用您的应用程序显示的第一个屏幕定义一个 activity 测试规则。

  @Rule
  public ActivityTestRule<MyFirstActivity> activityRule =
      new ActivityTestRule<>(MyFirstActivity.class);

当您使用注释 Rule 注释此 class 时,在每次测试开始之前,它将确保此 activity 已加载,然后您可以继续通过模仿用户交互到您想要的页面。如果您在应用此解决方案后仍有问题,请将其写为评论,因为那样您的问题将与同步相关,需要不同的答案。