Android 自动化测试中的 Espresso 谓词?
Android Espresso predicates in automated testing?
有什么方法可以实现类似于 iOS Xcode 的 NSPredicate
中的谓词以等待 Android Espresso 中的某些事件?
对于 iOS,您可以调用 expectationForPredicate
和 waitForExpectationsWithTimeout
作为 XCTest
的一部分。
我有两个类似的应用程序,用于 iOS 和 Android,我的任务是测试它们。在 iOS 中,我可以执行以下操作:
let app = XCUIApplication();
let condition = app.staticTexts["Text That Displays After Event"]
let exists = NSPredicate(format: "exists == true")
expectationForPredicate(exists, evaluatedWithObject: condition, handler: nil)
waitForExpectationsWithTimeout(5, handler: nil)
XCTAssert(app.staticTexts["Text That Displays After Event"].exists)
在Android中,我能做的最好的事情就是注册一个空闲资源并等待几秒钟,然后检查事件是否已手动发生。
long waitingTime = 3 * DateUtils.SECOND_IN_MILLIS;
IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
Espresso.registerIdlingResources(idlingResource);
onView(withId(R.id.id_in_next_event)).check(matches(isDisplayed()));
Espresso.unregisterIdlingResources(idlingResource);
我相信一定有更好的方法来做到这一点。
大多数事情都可以用 Instrumentation#waitForIdleSync()
方法完成。这将等待所有 UI 事件在继续之前得到处理。假设您有一个 ID 为 buttonId
的按钮,当按下该按钮时,它将显示一个 ID 为 hiddenView
的视图。
如果你这样做:
private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
@Test
public void checkViewIsRevealed() {
onView(withId(R.id.buttonId)).perform(click());
instrumentation.waitForIdleSync();
onView(withId(R.id.hiddenView)).check(matches(isDisplayed());
}
所以在这个过程中,执行 "click" 会在 hiddenView
上调用 View#setVisibility()
。然后,您等待空闲以确保所有 UI 操作都通过。然后检查是否显示了新视图。
您可以编写自定义 ViewAction
等待 Matcher<View>
变为真。
但这只有在 Espresso 不自行等待时才需要。 Espresso 等待,例如如果应用程序的主线程正在工作或者 AsyncTask
是 运行.
等待查看操作可以像这样实现:
public class WaitForCondition implements ViewAction {
private static final long IDLING_INTERVAL = 50;
private final Matcher<View> mCondition;
private final long mTimeout;
public WaitForCondition(final Matcher<View> condition, final long timeout) {
mCondition = condition;
mTimeout = timeout;
}
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(View.class);
}
@Override
public String getDescription() {
return String.format(Locale.US, "Waiting until the condition '%s' will become true (timeout %d ms)", getConditionDescription(), mTimeout);
}
private String getConditionDescription() {
StringDescription description = new StringDescription();
mCondition.describeTo(description);
return description.toString();
}
@Override
public void perform(final UiController uiController, final View view) {
if (mCondition.matches(view)) {
return;
}
final long timeOut = System.currentTimeMillis() + mTimeout;
// Wait for the condition to become true
while (System.currentTimeMillis() <= timeOut) {
uiController.loopMainThreadForAtLeast(IDLING_INTERVAL);
if (mCondition.matches(view)) {
return;
}
}
throw new PerformException.Builder()
.withActionDescription(this.getDescription())
.withViewDescription(HumanReadables.describe(view))
.withCause(new RuntimeException(String.format(Locale.US,
"Condition '%s' did not become true after %d ms of waiting.", getConditionDescription(), mTimeout)))
.build();
}
}
然后使用它:
onView(withId(R.id.id_in_next_event)).perform(new WaitForCondition(matches(isDisplayed()), 3000));
有什么方法可以实现类似于 iOS Xcode 的 NSPredicate
中的谓词以等待 Android Espresso 中的某些事件?
对于 iOS,您可以调用 expectationForPredicate
和 waitForExpectationsWithTimeout
作为 XCTest
的一部分。
我有两个类似的应用程序,用于 iOS 和 Android,我的任务是测试它们。在 iOS 中,我可以执行以下操作:
let app = XCUIApplication();
let condition = app.staticTexts["Text That Displays After Event"]
let exists = NSPredicate(format: "exists == true")
expectationForPredicate(exists, evaluatedWithObject: condition, handler: nil)
waitForExpectationsWithTimeout(5, handler: nil)
XCTAssert(app.staticTexts["Text That Displays After Event"].exists)
在Android中,我能做的最好的事情就是注册一个空闲资源并等待几秒钟,然后检查事件是否已手动发生。
long waitingTime = 3 * DateUtils.SECOND_IN_MILLIS;
IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
Espresso.registerIdlingResources(idlingResource);
onView(withId(R.id.id_in_next_event)).check(matches(isDisplayed()));
Espresso.unregisterIdlingResources(idlingResource);
我相信一定有更好的方法来做到这一点。
大多数事情都可以用 Instrumentation#waitForIdleSync()
方法完成。这将等待所有 UI 事件在继续之前得到处理。假设您有一个 ID 为 buttonId
的按钮,当按下该按钮时,它将显示一个 ID 为 hiddenView
的视图。
如果你这样做:
private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
@Test
public void checkViewIsRevealed() {
onView(withId(R.id.buttonId)).perform(click());
instrumentation.waitForIdleSync();
onView(withId(R.id.hiddenView)).check(matches(isDisplayed());
}
所以在这个过程中,执行 "click" 会在 hiddenView
上调用 View#setVisibility()
。然后,您等待空闲以确保所有 UI 操作都通过。然后检查是否显示了新视图。
您可以编写自定义 ViewAction
等待 Matcher<View>
变为真。
但这只有在 Espresso 不自行等待时才需要。 Espresso 等待,例如如果应用程序的主线程正在工作或者 AsyncTask
是 运行.
等待查看操作可以像这样实现:
public class WaitForCondition implements ViewAction {
private static final long IDLING_INTERVAL = 50;
private final Matcher<View> mCondition;
private final long mTimeout;
public WaitForCondition(final Matcher<View> condition, final long timeout) {
mCondition = condition;
mTimeout = timeout;
}
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(View.class);
}
@Override
public String getDescription() {
return String.format(Locale.US, "Waiting until the condition '%s' will become true (timeout %d ms)", getConditionDescription(), mTimeout);
}
private String getConditionDescription() {
StringDescription description = new StringDescription();
mCondition.describeTo(description);
return description.toString();
}
@Override
public void perform(final UiController uiController, final View view) {
if (mCondition.matches(view)) {
return;
}
final long timeOut = System.currentTimeMillis() + mTimeout;
// Wait for the condition to become true
while (System.currentTimeMillis() <= timeOut) {
uiController.loopMainThreadForAtLeast(IDLING_INTERVAL);
if (mCondition.matches(view)) {
return;
}
}
throw new PerformException.Builder()
.withActionDescription(this.getDescription())
.withViewDescription(HumanReadables.describe(view))
.withCause(new RuntimeException(String.format(Locale.US,
"Condition '%s' did not become true after %d ms of waiting.", getConditionDescription(), mTimeout)))
.build();
}
}
然后使用它:
onView(withId(R.id.id_in_next_event)).perform(new WaitForCondition(matches(isDisplayed()), 3000));