安卓Espresso-如果未选中复选框,则单击复选框

27

我有一个onView(withId(R.id.check_box)).perform(click()),但只有在复选框未被选中时才想执行此操作。在Espresso中如何实现?

6个回答

56

我也希望根据之前的状态来切换checkbox/switch。一开始,我尝试这样将关闭的复选框切换为打开:

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

...并使用此方法将已打开的复选框切换为关闭:

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

然而,这并不起作用,因为Espresso在执行操作之前将查找特定的切换状态。有时,您事先不知道它是开还是关。

我的解决方案是使用自定义ViewAction关闭/打开任何可选对象(开关、复选框等),而不依赖于先前的状态。所以,如果它已经开启,它将保持开启状态。如果它关闭了,它将切换为开启状态。以下是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);
        }
    };
}

以下是如何使用它的步骤(在这个例子中,当你想要切换到“开启”状态时):

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

非常好,谢谢@FrostRocket。我唯一需要更改的是删除“scrollTo()”ViewAction,因为我收到了错误提示,告诉我我的复选框不支持scrollTo:onView(withId(R.id.global_search)).perform(setChecked(globalSearch));正如您所看到的,我想将复选框设置为提供的“globalSearch”变量的值。 - yvolk
我更新了答案,添加了对复选框当前状态的检查,并仅在其状态不符合要求时更改它,以避免不必要的事件: if (checkableView.isChecked() != checked) { checkableView.setChecked(checked); } - yvolk
setChecked的内部实现已经考虑了这一点。 - 0xMatthewGroves
这是一个很好的选择。但是,请注意,如果您正在基于复选框的点击执行活动中的操作,则不会触发它。 - Stonz2
@Stonz2说得很对——setChecked()不会触发监听器操作。为了解决这个问题,可以点击视图:if (checkableView.isChecked() != checked) click().perform(uiController, view); - Jerry101

7

正如@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
    }
}

2

看起来这是你的测试的一部分,需要勾选复选框。

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

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

如果失败,您的测试将失败,在您的情况下可能是好事。然后,在此情况匹配后,您可以执行所需的单击操作。
如果这不是您想要的情况,您可以在Espresso测试中进一步解释您试图做什么吗?

我想确保在进行测试前复选框已被选中。它可能未被选中的原因是另一个测试使其处于未选中状态。 - kyrax
Espresso不应该能够设置任何视图的状态或以任何方式操作视图。如果没有其他方法来构建您的测试,那么之前的答案可以工作,但这又违反了Espresso的惯例。我建议以一种使它们更模块化的方式构建您的测试,以便它们不必操纵任何视图。@kyrax - Sahith Reddy
1
@kyrax,你能否接受我的答案作为被采纳的答案,因为应该知道在 Espresso 测试中使用 Activity 是非常糟糕的。 - Sahith Reddy
1
@kryax 我不确定为什么这个答案被接受了,因为它并没有解决原问题。如果你想在不失败测试的情况下操作视图,请查看我发布的解决方案。 - 0xMatthewGroves

1
这个简单的解决方案可能会对你有帮助。
onView(withId(R.id.checkBox_tea)).check(matches(isNotChecked())).perform(click()).check(matches(isChecked()));

对我来说完美地工作。


我不明白为什么这不是被接受的答案... 这是唯一一个没有进行任何操作/覆盖的答案,而且只需要一行代码,没有额外的类。 - Michiel van der Blonk

0
你的代码中有类似这样的内容:
@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())    
}

3
根据Google的说法,这并不被推荐,因为它违反了Espresso的工作原理,并且非常危险。https://youtu.be/isihPOY2vS4?t=4m12s - Sahith Reddy

0

我曾经遇到过同样的问题,编写了自己的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())

现在我可以确定找到的开关始终是未选中的,无论之前它是否被选中或未选中。

不要实现Matcher,而是扩展BaseMatcher。 - Achraf Amil

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