首页 > 解决方案 > Espresso 测试,点击 Chip view not working

问题描述

我有一个ChipGroup包含一些Chip观点,这些视图在检查时,显示一些TextViews视图SeekBars。我正在尝试使用 Espresso 对此进行测试,但结果很奇怪。

因为下面的测试失败了:(Test1)

@Test
fun whenEuropeChipIsSelected_DisplaySeekBarsAndTextViews() {
    launchFragmentInContainer<GameConfigSurvivalFragment>()

    onView(withId(R.id.europeSurvChip)).perform(click())

    onView(withId(R.id.countriesNumberSeekBar)).check(matches(isDisplayed()))
    onView(withId(R.id.selectCountriesNumberTv)).check(matches(isDisplayed()))
    onView(withId(R.id.selectedCountriesTV)).check(matches(isDisplayed()))
    onView(withId(R.id.timeLimitSeekBar)).check(matches(isDisplayed()))
    onView(withId(R.id.timeLimitTv)).check(matches(isDisplayed()))
    onView(withId(R.id.selectTimeLimitTv)).check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}

我创建了以下简单测试以查看发生了什么:(Test2)也失败了。(见下面的堆栈跟踪)

@Test
fun whenEuropeChipIsClicked_CheckIsSelected() {
    launchFragmentInContainer<GameConfigSurvivalFragment>()

    onView(withId(R.id.europeSurvChip)).perform(click())

    onView(withId(R.id.europeSurvChip)).check(matches(isChecked()))
}

我为此功能的一些生产代码如下所示,我插入了一些日志以查看chip单击芯片时的状态:

binding.europeSurvChip.setOnCheckedChangeListener { isChecked ->
        if (isChecked) {
            counter.value = counter.value!!.plus(Europe.totalCountries)
            continentsList.add(Europe)
            Log.d("ChipSelected", "EuropeChipSelected: isChecked: ${europeSurvChip.isChecked}")
        } else {
            counter.value = counter.value!!.minus(Europe.totalCountries)
            continentsList.remove(Europe)
            Log.d("ChipSelected", "EuropeChipSelected: isChecked: ${europeSurvChip.isChecked}")
        }
    }

在模拟器上测试Chip手动选择时,我达到了预期的结果,并且我可以在日志中看到Chip已选中和未选中。但是,我创建的 Espresso 测试从未通过。手动测试一切都按预期工作,SeekbarsTextViews显示 和 以及Chip正在检查的按钮。

我禁用了动画,因为其他问题向患有Espresso click()方法无法正常工作的人提出了建议。

知道我在做什么错吗?(一切都在主线程中发生)

我不自信的想法:


Test2 的堆栈跟踪

    E/TestRunner: failed: whenEuropeChipIsClicked_CheckIsSelected(com.example.capitalcityquizktx.ui.survivalmode.config.GameConfigSurvivalFragmentTest)
    ----- begin exception -----
E/TestRunner: androidx.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'with checkbox state: is <true>' doesn't match the selected view.
    Expected: with checkbox state: is <true>
         Got: "Chip{id=2131230840, res-name=europeSurvChip, visibility=VISIBLE, width=315, height=88, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, layout-params=android.widget.TableRow$LayoutParams@b19ff14, tag=null, root-is-layout-requested=false, has-input-connection=false, x=11.0, y=11.0, text=Europe, input-type=0, ime-target=false, has-links=false, is-checked=false}"

        at dalvik.system.VMStack.getThreadStackTrace(Native Method)
        at java.lang.Thread.getStackTrace(Thread.java:1720)
        at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:94)
        at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:57)
        at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:318)
        at androidx.test.espresso.ViewInteraction.check(ViewInteraction.java:300)
        at com.example.capitalcityquizktx.ui.survivalmode.config.GameConfigSurvivalFragmentTest.whenEuropeChipIsClicked_CheckIsSelected(GameConfigSurvivalFragmentTest.kt:63)
        at java.lang.reflect.Method.invoke(Native Method)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
        at androidx.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
        at androidx.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:527)
        at org.junit.rules.RunRules.evaluate(RunRules.java:20)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104)
        at org.junit.runners.Suite.runChild(Suite.java:128)
        at org.junit.runners.Suite.runChild(Suite.java:27)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
        at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
        at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388)
        at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
     Caused by: junit.framework.AssertionFailedError: 'with checkbox state: is <true>' doesn't match the selected view.
    Expected: with checkbox state: is <true>
         Got: "Chip{id=2131230840, res-name=europeSurvChip, visibility=VISIBLE, width=315, height=88, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layou
    ----- end exception -----

Test1 的 stackTrace

    E/TestRunner: failed: whenEuropeChipIsSelected_DisplaySeekBarsAndTextViews_numberOfCountriesAndTimeLimit(com.example.capitalcityquizktx.ui.survivalmode.config.GameConfigSurvivalFragmentTest)
    ----- begin exception -----
E/TestRunner: androidx.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'is displayed on the screen to the user' doesn't match the selected view.
    Expected: is displayed on the screen to the user
         Got: "SeekBar{id=2131230817, res-name=countriesNumberSeekBar, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=true, is-selected=false, layout-params=androidx.constraintlayout.widget.ConstraintLayout$LayoutParams@1f49905, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0}"

        at dalvik.system.VMStack.getThreadStackTrace(Native Method)
        at java.lang.Thread.getStackTrace(Thread.java:1720)
        at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:94)
        at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:57)
        at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:318)
        at androidx.test.espresso.ViewInteraction.check(ViewInteraction.java:300)
        at com.example.capitalcityquizktx.ui.survivalmode.config.GameConfigSurvivalFragmentTest.whenEuropeChipIsSelected_DisplaySeekBarsAndTextViews_numberOfCountriesAndTimeLimit(GameConfigSurvivalFragmentTest.kt:49)
        at java.lang.reflect.Method.invoke(Native Method)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
        at androidx.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
        at androidx.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:527)
        at org.junit.rules.RunRules.evaluate(RunRules.java:20)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104)
        at org.junit.runners.Suite.runChild(Suite.java:128)
        at org.junit.runners.Suite.runChild(Suite.java:27)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
        at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
        at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388)
        at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
     Caused by: junit.framework.AssertionFailedError: 'is displayed on the screen to the user' doesn't match the selected view.
    Expected: is displayed on the screen to the user
         Got: "SeekBar{id=2131230817, res-name=countriesNumberSeekBar, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=fals
    ----- end exception -----


生产代码类。(它按预期工作):

class GameConfigSurvivalFragment : Fragment(),
GameConfigSurvivalView {

override val continentsList = MutableLiveData<List<Continent>>().default(arrayListOf())
private val displayTimeLimitSeekBar = MutableLiveData<Boolean>().default(false)
private val displayQuestionNumberSeekBar = MutableLiveData<Boolean>().default(false)

override fun showQuestionsNumberSelection() {
    displayQuestionNumberSeekBar.value = true
}

override fun showTimeLimitSelection() {
    displayTimeLimitSeekBar.value = true
}

override fun hideQuestionsNumberSelection() {
    displayQuestionNumberSeekBar.value = false
}

override fun hideTimeLimitSelection() {
    displayTimeLimitSeekBar.value = false
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val binding: GameConfigSurvivalFragmentBinding = inflate(
        inflater, R.layout.game_config_survival_fragment, container, false
    )

    val presenter = GameConfigSurvivalPresenter(this)
    presenter.receiveContinentSelection()

    //Minimum amount of seconds that will be added to timeLimitSeekbar
    val minTimeLimit = 5

    // This counter is used to count the amount of countries to set up the seekBar acording to its value
    val counter = MutableLiveData<Int>()
    counter.value = 0

    // This val is used to gather information about the number of chips that are checked
    // in order to show the seekBar if there are any of them checked
    val continentsSelected = MutableLiveData<Int>()
    continentsSelected.value = 0

    displayQuestionNumberSeekBar.observe(this,
        Observer { displayIt ->
            if (displayIt){
                binding.selectCountriesNumberTv.isVisible = true
                binding.countriesNumberSeekBar.isVisible = true
                binding.selectedCountriesTV.isVisible = true
                binding.countriesNumberSeekBar.max = counter.value!!
                binding.countriesNumberSeekBar.progress = binding.countriesNumberSeekBar.max
            }else{
                binding.selectCountriesNumberTv.isVisible = false
                binding.countriesNumberSeekBar.isVisible = false
                binding.selectedCountriesTV.isVisible = false
            }
        })

    displayTimeLimitSeekBar.observe(this,
        Observer { displayIt ->
            if (displayIt){
                binding.selectTimeLimitTv.isVisible = true
                binding.timeLimitSeekBar.isVisible = true
                binding.timeLimitTv.isVisible = true
            }else{
                binding.selectTimeLimitTv.isVisible = false
                binding.timeLimitSeekBar.isVisible = false
                binding.timeLimitTv.isVisible = false
            }
        })

    /* The following listeners check the state of the chips whether they are checked or not,
    in order to count the number of countries and to count the number of continents selected */

    binding.europeSurvChip.setOnCheckedChangeListener { buttonView, isChecked ->
        if (isChecked) {
            counter.value = counter.value!!.plus(Europe.totalCountries)
            continentsList.add(Europe)
            Log.d("ChipSelected", "EuropeChipSelected: isChecked: ${europeSurvChip.isChecked}")
        } else {
            counter.value = counter.value!!.minus(Europe.totalCountries)
            continentsList.remove(Europe)
            Log.d("ChipSelected", "EuropeChipSelected: isChecked: ${europeSurvChip.isChecked}")
        }
    }

    return binding.root
  }
}

标签: androidandroid-espresso

解决方案


Chip通过使XML 文件中的可点击和可检查来修复它。

我猜它是在代码中的某处以编程方式使其可点击和可检查的。


推荐阅读