当许多元素处于层次结构中时,Espresso 匹配找到的第一个元素
Espresso match first element found when many are in hierarchy
我正在尝试编写一个 espresso 函数来匹配 espresso 根据我的函数找到的第一个元素,即使找到多个匹配项也是如此。
例如:
我有一个列表视图,其中包含包含商品价格的单元格。我希望能够将货币转换为加元并验证商品价格是否以加元表示。
我正在使用这个功能:
onView(anyOf(withId(R.id.product_price), withText(endsWith("CAD"))))
.check(matches(
isDisplayed()));
这将引发 AmbiguousViewMatcherException。
在这种情况下,我不关心有多少单元格显示CAD,我只是想验证它是否显示。有没有办法让espresso一遇到满足参数的物体就通过这个测试呢?
您应该能够使用以下代码创建仅匹配第一项的自定义匹配器:
private <T> Matcher<T> first(final Matcher<T> matcher) {
return new BaseMatcher<T>() {
boolean isFirst = true;
@Override
public boolean matches(final Object item) {
if (isFirst && matcher.matches(item)) {
isFirst = false;
return true;
}
return false;
}
@Override
public void describeTo(final Description description) {
description.appendText("should return first matching item");
}
};
}
据我了解,在您的情况下,在您转换货币后所有价格都应以加元为单位。因此,只需抓住第一项并验证它也应该可以解决您的问题:
onData(anything())
.atPosition(0)
.onChildView(allOf(withId(R.id.product_price), withText(endsWith("CAD"))))
.check(matches(isDisplayed()));
我创建了这个匹配器,以防你有许多具有相同特征的元素,比如相同的 id,如果你不仅想要第一个元素,还想要一个 具体元素。希望这会有所帮助:
private static Matcher<View> getElementFromMatchAtPosition(final Matcher<View> matcher, final int position) {
return new BaseMatcher<View>() {
int counter = 0;
@Override
public boolean matches(final Object item) {
if (matcher.matches(item)) {
if(counter == position) {
counter++;
return true;
}
counter++;
}
return false;
}
@Override
public void describeTo(final Description description) {
description.appendText("Element at hierarchy position "+position);
}
};
}
示例:
您有许多按钮具有从您正在使用的库中给定的相同 ID,您想要选择第二个按钮。
ViewInteraction colorButton = onView(
allOf(
getElementFromMatchAtPosition(allOf(withId(R.id.color)), 2),
isDisplayed()));
colorButton.perform(click());
对于遇到我刚刚遇到的相同问题的任何人:如果您使用 Matcher @appmattus 共享来执行一个 ViewAction,那会很好,但是在执行多个 ViewAction 时,它不会:
onView(first(allOf(matcher1(), matcher2()))
.perform(viewAction1(), viewAction2())
viewAction1
将被执行,但在执行 vewAction2
之前,匹配器会再次求值,isFirst
将始终为 return false。您将收到一条错误消息,指出没有匹配的视图。
所以,这里有一个使用多个 ViewActions 的版本,并且 return 不仅有第一个,还有第二个或第三个(或...),如果您愿意的话:
class CountViewMatcher(val count: Int) : BaseMatcher<View>() {
private var matchingCount = 0
private var matchingViewId: Int? = null
override fun matches(o: Any): Boolean {
if (o is View) {
if (matchingViewId != null) {
// A view already matched the count
return matchingViewId == o.id
} else {
matchingCount++
if (count == matchingCount) {
matchingViewId = o.id
return true
} else {
return false
}
}
} else {
// o is not a view.
return false
}
}
}
我正在尝试编写一个 espresso 函数来匹配 espresso 根据我的函数找到的第一个元素,即使找到多个匹配项也是如此。
例如: 我有一个列表视图,其中包含包含商品价格的单元格。我希望能够将货币转换为加元并验证商品价格是否以加元表示。
我正在使用这个功能:
onView(anyOf(withId(R.id.product_price), withText(endsWith("CAD"))))
.check(matches(
isDisplayed()));
这将引发 AmbiguousViewMatcherException。
在这种情况下,我不关心有多少单元格显示CAD,我只是想验证它是否显示。有没有办法让espresso一遇到满足参数的物体就通过这个测试呢?
您应该能够使用以下代码创建仅匹配第一项的自定义匹配器:
private <T> Matcher<T> first(final Matcher<T> matcher) {
return new BaseMatcher<T>() {
boolean isFirst = true;
@Override
public boolean matches(final Object item) {
if (isFirst && matcher.matches(item)) {
isFirst = false;
return true;
}
return false;
}
@Override
public void describeTo(final Description description) {
description.appendText("should return first matching item");
}
};
}
据我了解,在您的情况下,在您转换货币后所有价格都应以加元为单位。因此,只需抓住第一项并验证它也应该可以解决您的问题:
onData(anything())
.atPosition(0)
.onChildView(allOf(withId(R.id.product_price), withText(endsWith("CAD"))))
.check(matches(isDisplayed()));
我创建了这个匹配器,以防你有许多具有相同特征的元素,比如相同的 id,如果你不仅想要第一个元素,还想要一个 具体元素。希望这会有所帮助:
private static Matcher<View> getElementFromMatchAtPosition(final Matcher<View> matcher, final int position) {
return new BaseMatcher<View>() {
int counter = 0;
@Override
public boolean matches(final Object item) {
if (matcher.matches(item)) {
if(counter == position) {
counter++;
return true;
}
counter++;
}
return false;
}
@Override
public void describeTo(final Description description) {
description.appendText("Element at hierarchy position "+position);
}
};
}
示例:
您有许多按钮具有从您正在使用的库中给定的相同 ID,您想要选择第二个按钮。
ViewInteraction colorButton = onView(
allOf(
getElementFromMatchAtPosition(allOf(withId(R.id.color)), 2),
isDisplayed()));
colorButton.perform(click());
对于遇到我刚刚遇到的相同问题的任何人:如果您使用 Matcher @appmattus 共享来执行一个 ViewAction,那会很好,但是在执行多个 ViewAction 时,它不会:
onView(first(allOf(matcher1(), matcher2()))
.perform(viewAction1(), viewAction2())
viewAction1
将被执行,但在执行 vewAction2
之前,匹配器会再次求值,isFirst
将始终为 return false。您将收到一条错误消息,指出没有匹配的视图。
所以,这里有一个使用多个 ViewActions 的版本,并且 return 不仅有第一个,还有第二个或第三个(或...),如果您愿意的话:
class CountViewMatcher(val count: Int) : BaseMatcher<View>() {
private var matchingCount = 0
private var matchingViewId: Int? = null
override fun matches(o: Any): Boolean {
if (o is View) {
if (matchingViewId != null) {
// A view already matched the count
return matchingViewId == o.id
} else {
matchingCount++
if (count == matchingCount) {
matchingViewId = o.id
return true
} else {
return false
}
}
} else {
// o is not a view.
return false
}
}
}