espresso 虽然有六十秒的超时等待,但是如果布局中没有匹配到的控件,它会立即报错(布局延迟或者是网络请求后才会加载布局它会报错)。
所以我引入了超时工具类:TimeoutEspresso
方法介绍:
工具类方法是跟Espresso 的方法是对应的,内部其实是一个超时循环,
onViewWithTimeout => onView
checkWithTimeout => check 内部实现同上
performWithTimeout=>perform
- 查找和验证控件推荐使用Timeout 工具类.
- 对view 进行操作,推荐Espresso 的原生onView()check()和perform() 内部实现同上
使用例子:
使用espresso 对一个字符是“确定”的控件进行验证点击:
使用TimeoutEspresso (默认十秒超时)点击:
- 查找和验证控件推荐使用Timeout 工具类.
- 对view 进行操作,推荐Espresso 的原生onView()check()和perform()
使用TimeoutEspresso (自定义十五秒超时)点击:
操作控件容易频繁点击,推荐写法:
工具类:
import android.support.annotation.NonNull;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.FailureHandler;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.Root;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.ViewAssertion;
import android.support.test.espresso.ViewInteraction;
import android.support.test.espresso.assertion.ViewAssertions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.view.View;
import junit.framework.AssertionFailedError;
import org.hamcrest.Matcher;
/**
* @author DrChen
* @Date 2020/6/3. qq:1414355045 用以应对Espresso布局延时加载,View no matcher 的情况
*/
public class TimeoutEspresso {
private static final int SLEEP_IN_A_LOOP_TIME = 50;
private static final long DEFAULT_TIMEOUT_IN_MILLIS = 10 * 1000L;
/**
* Use instead of {@link Espresso#onView(Matcher)}
*
* @param timeoutInMillis timeout after which an error is thrown
* @param viewMatcher view matcher to check for view
* @return view interaction
*/
public static TimedViewInteraction onViewWithTimeout(long timeoutInMillis,
@NonNull final Matcher<View> viewMatcher) {
final long startTime = System.currentTimeMillis();
final long endTime = startTime + timeoutInMillis;
do {
try {
ViewInteraction viewInteraction = Espresso.onView(viewMatcher);
// viewInteraction.check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
return new TimedViewInteraction(viewInteraction);
} catch (NoMatchingViewException ex) {
SystemClock.sleep(1000);
//ignore
}
}
while (System.currentTimeMillis() < endTime);
// timeout happens
return new TimedViewInteraction(Espresso.onView(viewMatcher));//返回最后一次报错的内容
}
/**
* Use instead of {@link Espresso#onView(Matcher)}. Same as {@link #onViewWithTimeout(long,
* Matcher)} but with the default timeout {@link #DEFAULT_TIMEOUT_IN_MILLIS}.
*
* @param viewMatcher view matcher to check for view
* @return view interaction
*/
public static TimedViewInteraction onViewWithTimeout(@NonNull final Matcher<View> viewMatcher) {
return onViewWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewMatcher);
}
/**
* A wrapper around {@link ViewInteraction} which allows to set timeouts for view actions and
* assertions.
*/
public static class TimedViewInteraction {
private ViewInteraction wrappedViewInteraction;
public TimedViewInteraction(ViewInteraction wrappedViewInteraction) {
this.wrappedViewInteraction = wrappedViewInteraction;
}
/**
* @see ViewInteraction#perform(ViewAction...)
*/
public TimedViewInteraction perform(final ViewAction... viewActions) {
wrappedViewInteraction.perform(viewActions);
return this;
}
/**
* {@link ViewInteraction#perform(ViewAction...)} with a timeout of {@link
* #DEFAULT_TIMEOUT_IN_MILLIS}.
*
* @see ViewInteraction#perform(ViewAction...)
*/
public TimedViewInteraction performWithTimeout(final ViewAction... viewActions) {
return performWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewActions);
}
/**
* {@link ViewInteraction#perform(ViewAction...)} with a timeout.
*
* @see ViewInteraction#perform(ViewAction...)
*/
public TimedViewInteraction performWithTimeout(long timeoutInMillis,
final ViewAction... viewActions) {
final long startTime = System.currentTimeMillis();
final long endTime = startTime + timeoutInMillis;
do {
try {
return perform(viewActions);
} catch (RuntimeException ex) {
//ignore
}
//view 的操作间隔1000 避免过快的重复点击
SystemClock.sleep(1000);
}
while (System.currentTimeMillis() < endTime);
return perform(viewActions);//返回最后一次报错的内容
}
/**
* @see ViewInteraction#withFailureHandler(FailureHandler)
*/
public TimedViewInteraction withFailureHandler(FailureHandler failureHandler) {
wrappedViewInteraction.withFailureHandler(failureHandler);
return this;
}
/**
* @see ViewInteraction#inRoot(Matcher)
*/
public TimedViewInteraction inRoot(Matcher<Root> rootMatcher) {
wrappedViewInteraction.inRoot(rootMatcher);
return this;
}
/**
* @see ViewInteraction#check(ViewAssertion)
*/
public TimedViewInteraction check(final ViewAssertion viewAssert) {
wrappedViewInteraction.check(viewAssert);
return this;
}
/**
* {@link ViewInteraction#check(ViewAssertion)} with a timeout of {@link
* #DEFAULT_TIMEOUT_IN_MILLIS}.
*
* @see ViewInteraction#check(ViewAssertion)
*/
public TimedViewInteraction checkWithTimeout(final ViewAssertion viewAssert) {
return checkWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewAssert);
}
/**
* {@link ViewInteraction#check(ViewAssertion)} with a timeout.
*
* @see ViewInteraction#check(ViewAssertion)
*/
public TimedViewInteraction checkWithTimeout(long timeoutInMillis,
final ViewAssertion viewAssert) {
final long startTime = System.currentTimeMillis();
final long endTime = startTime + timeoutInMillis;
do {
try {
return check(viewAssert);
} catch (RuntimeException ex) {
//ignore
}catch (AssertionFailedError error){
}
SystemClock.sleep(SLEEP_IN_A_LOOP_TIME);
}
while (System.currentTimeMillis() < endTime);
return check(viewAssert);//返回最后一次报错的内容
}
}
}