【问题标题】:Android Espresso - Click checkbox if not checkedAndroid Espresso - 如果未选中,请单击复选框
【发布时间】:2016-10-15 14:45:26
【问题描述】:

我有onView(withId(R.id.check_box)).perform(click()),但我只想在未选中复选框的情况下执行此操作。如何在浓缩咖啡中做到这一点?

【问题讨论】:

    标签: android testing checkbox android-espresso


    【解决方案1】:

    你的代码中有这样的东西:

    @Rule
    public ActivityTestRule<ActivityMain> ActivityMainRule = new ActivityTestRule<>(ActivityMain.class);
    

    试试

    if (!ActivityMainRule.getActivity().findViewById(R.id.check_box).isChecked()) {
        onView(withId(R.id.check_box)).perform(click())    
    }
    

    【讨论】:

    【解决方案2】:

    这似乎是您测试的一部分,需要选中复选框。

    您可以通过以下方式检查:

    onView(withId(R.id.checkbox)).check(matches(not(isChecked())));
    

    如果失败,您的测试将失败,这对您来说可能是件好事。然后,您可以在这种情况匹配后执行您想要的点击。

    如果这不是您想要的情况,您能否详细解释一下您在此 Espresso 测试中尝试做的事情?

    【讨论】:

    • 我想确保在进行测试之前选中该复选框。它可能未选中的原因是因为另一个测试未选中它。
    • Espresso 不应该能够设置任何视图状态或以任何方式操作视图。如果没有其他方法可以构建您的测试,那么之前的答案将起作用,但又违反了 espresso 约定。我建议以一种使它们更加模块化的方式来构建你的测试,这样它们就不必操纵任何视图。 @kyrax
    • @kyrax 你能接受我的回答吗?因为应该知道在浓缩咖啡测试中使用活动是非常非常糟糕的。
    • @kryax 我不确定为什么这是公认的答案,因为这不能解决原始问题。如果您想在不通过测试的情况下操作视图,请查看我发布的解决方案。
    【解决方案3】:

    我有同样的问题,我写了一个自己的 ViewAction 总是取消选中我的 SwitchCompat

    class UncheckViewAction implements ViewAction{
    
        @Override
        public Matcher<View> getConstraints() {
            return new Matcher<View>() {
                @Override
                public boolean matches(Object item) {
                    return isA(SwitchCompat.class).matches(item);
                }
    
                @Override
                public void describeMismatch(Object item, Description mismatchDescription) {
    
                }
    
                @Override
                public void _dont_implement_Matcher___instead_extend_BaseMatcher_() {
    
                }
    
                @Override
                public void describeTo(Description description) {
    
                }
            };
        }
    
        @Override
        public String getDescription() {
            return null;
        }
    
        @Override
        public void perform(UiController uiController, View view) {
            SwitchCompat scView = (SwitchCompat) view;
            scView.setChecked(false);
        }
    }
    

    并像这样使用它:

    onView(withId(R.id.check_box)).perform(new UncheckViewAction())
    

    现在我可以确定找到的开关始终处于未选中状态,无论之前是选中还是未选中。

    【讨论】:

    • dont_implement_Matcher___instead_extend_BaseMatcher
    【解决方案4】:

    我还想根据之前的状态切换复选框/开关。起初,我尝试用这个来打开一个关闭的复选框:

    onView(withId(R.id.checkbox)).check(matches(isNotChecked())).perform(scrollTo(), click());
    

    ...这将关闭一个已打开的复选框:

    onView(withId(R.id.checkbox)).check(matches(isChecked())).perform(scrollTo(), click());
    

    但是,这不起作用,因为 Espresso 会在执行操作之前寻找特定的切换状态。有时,您不知道它是打开还是关闭。

    我的解决方案是使用自定义 ViewAction 来关闭/打开任何不依赖于先前状态的可检查对象(开关、复选框等)。因此,如果它已经打开,它将保持打开状态。如果它关闭,它将切换到 ON。这是 ViewAction:

    public static ViewAction setChecked(final boolean checked) {
        return new ViewAction() {
            @Override
            public BaseMatcher<View> getConstraints() {
                return new BaseMatcher<View>() {
                    @Override
                    public boolean matches(Object item) {
                        return isA(Checkable.class).matches(item);
                    }
    
                    @Override
                    public void describeMismatch(Object item, Description mismatchDescription) {}
    
                    @Override
                    public void describeTo(Description description) {}
                };
            }
    
            @Override
            public String getDescription() {
                return null;
            }
    
            @Override
            public void perform(UiController uiController, View view) {
                Checkable checkableView = (Checkable) view;
                checkableView.setChecked(checked);
            }
        };
    }
    

    下面是你如何使用它(在这个例子中,当你想要切换到 ON 时):

    onView(withId(R.id.toggle)).perform(scrollTo(), setChecked(true));
    

    【讨论】:

    • 完美,谢谢@FrostRocket 我唯一需要更改的是删除“scrollTo()” ViewAction,因为我收到错误消息,提示我的复选框不支持 scrollTo:onView(withId(R.id.global_search)).perform(setChecked(globalSearch)); 就像你一样看,我想将复选框设置为提供的“globalSearch”变量的值
    • 我更新了答案,添加了对复选框当前状态的检查,并仅在其状态不是必需时才更改它以避免不必要的事件:if (checkableView.isChecked() != checked) { checkableView.setChecked(checked); }
    • setChecked 的内部实现已经考虑到了这一点。
    • 这是一个绝佳的选择。但是,请注意,如果您在活动中基于被单击的复选框执行操作,则不会触发它。
    • @Stonz2 有一个好处——setChecked() 不会触发侦听器操作。要解决此问题,请改为单击视图:if (checkableView.isChecked() != checked) click().perform(uiController, view);
    【解决方案5】:

    正如@FrostRocket 建议的那样,但用 Kotlin 编写。

    我们定义了一个只能对可检查项目执行的自定义操作(在约束中指定)。所以我们安全地将视图转换为 Checkable 以访问 setCheckable 方法。

    fun setChecked(checked: Boolean) = object : ViewAction {
        val checkableViewMatcher = object : BaseMatcher<View>() {
            override fun matches(item: Any?): Boolean = isA(Checkable::class.java).matches(item)
            override fun describeTo(description: Description?) {
                description?.appendText("is Checkable instance ")
            }
        }
    
        override fun getConstraints(): BaseMatcher<View> = checkableViewMatcher
        override fun getDescription(): String? = null
        override fun perform(uiController: UiController?, view: View) {
            val checkableView: Checkable = view as Checkable
            checkableView.isChecked = checked
        }
    }
    

    【讨论】:

      【解决方案6】:

      这个简单的解决方案可能会对您有所帮助

      onView(withId(R.id.checkBox_tea)).check(matches(isNotChecked())).perform(click()).check(matches(isChecked()));
      

      非常适合我。

      【讨论】:

      • 我不明白这不是公认的答案......它是唯一没有任何操作/覆盖的答案,它只需要一行,不需要额外的类
      猜你喜欢
      • 2019-04-01
      • 2014-03-21
      • 2021-09-18
      • 1970-01-01
      • 2013-12-06
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多