总目录:Espresso从开始到…
如果你看了上一篇中对一些常用方法的介绍,想必现在已经可以对Espresso正常进行使用了,从这里开始我们开始看一些“简单”的东西。了解一下这三部曲是什么。
onView(ViewMatcher)
.perform(ViewAction)
.check(ViewAssertion);
从onView()&onData()开始
作为一个程序猿自然不能满足于三部曲,虽然不能什么都一清二楚,但是最差也要知道大概的流程吧。不然都不好意思说自己用过Espresso。所以与 Espresso 的故事就是从 Ctrl 打开 onView()
开始了。
这里直接进入 Espresso 类, 这里主要有几个常用的静态工具函数
函数名 | 功能 |
---|---|
pressBack() |
返回键 |
closeSoftKeyboard() |
关闭软键盘 |
openActionBarOverflowOrOptionsMenu() |
菜单键 |
openContextualActionModeOverflowMenu(); |
实体键盘菜单键 |
还有几个registerIdlingResources
、unregisterIdlingResources
等关于IdlingResources的函数。以及本文的关键onView()
和onData()
,在这里分别生成了ViewInteraction
和DataInteraction
。
public static ViewInteraction onView(final Matcher<View> viewMatcher) {
return BASE.plus(new ViewInteractionModule(viewMatcher)).viewInteraction();
}
注:onView 在这里使用了 Dagger 框架 ,由于笔者没有使用过该框架,在这里就不多赘述,感兴趣的可以自行查阅。
public static DataInteraction onData(Matcher<? extends Object> dataMatcher) {
return new DataInteraction(dataMatcher);
}
ViewInteraction
ViewInteraction
中的公共函数非常少,只有四个:
函数名 | 功能 |
---|---|
perform() |
执行ViewAction操作 |
check() |
检查ViewAssertion |
inRoot() |
确定目标view所在的root |
withFailureHandler() |
提供错误处理方式 |
1.perform()
我们还是按照三部曲的顺序进行先看perform()
:
public ViewInteraction perform(final ViewAction... viewActions) {
checkNotNull(viewActions);
for (ViewAction action : viewActions) {
doPerform(action);
}
return this;
}
从方法的形参ViewAction... viewActions
我们可以知道perform()
是支持同时执行多个操作的,但是会通过doPerform(action)
按照顺序依次执行。
到这里问题就来了,如果按照三部曲的理解来说,现在应该开始对控件执行操作了,但是需要操作的控件在哪?我们至今没有看到,难道在onView()
初始化的过程中已经将View
检索出来存储为成员变量了?ok,我们来看一下 ViewInteraction 有哪些成员变量:
//用于进行简单UI操作的工具
private final UiController uiController;
//用于查找View的工具
private final ViewFinder viewFinder;
//执行已提交 runnable 任务的对象
private final Executor mainThreadExecutor;
//错误处理机制与 withFailureHandler() 有关
private volatile FailureHandler failureHandler;
//view的匹配器(我们在onView(viewMatcher)传入的)
private final Matcher<View> viewMatcher;
//缺点查询 view 的 root 与 inRoot() 有关
private final AtomicReference<Matcher<Root>> rootMatcherRef;
好吧,现实并不是想象的那样,ViewInteraction 并没有存储 view ,里面只有用于查找 view 的工具(ViewFinder
)和材料(Matcher<View>
)。看来答案需要在接下来的doPerform(action)
中寻找了。让我们看一下代码:
private void doPerform(final ViewAction viewAction) {
checkNotNull(viewAction);
final Matcher<? extends View> constraints = checkNotNull(viewAction.getConstraints());
runSynchronouslyOnUiThread(new Runnable() {
@Override
public void run() {
uiController.loopMainThreadUntilIdle();
View targetView = viewFinder.getView();
Log.i(TAG, String.format(
"Performing '%s' action on view %s", viewAction.getDescription(), viewMatcher));
if (!constraints.matches(targetView)) {
// TODO(user): update this to describeMismatch once hamcrest is updated to new
StringDescription stringDescription = new StringDescription(new StringBuilder(
"Action will not be performed because the target view "
+ "does not match one or more of the following constraints:\n"));
constraints.describeTo(stringDescription);
stringDescription.appendText("\nTarget view: ")
.appendValue(HumanReadables.describe(targetView));
if (viewAction instanceof ScrollToAction
&& isDescendantOfA(isAssignableFrom((AdapterView.class))).matches(targetView)) {
stringDescription.appendText(
"\nFurther Info: ScrollToAction on a view inside an AdapterView will not work. "
+ "Use Espresso.onData to load the view.");
}
throw new PerformException.Builder()
.withActionDescription(viewAction.getDescription())
.withViewDescription(viewMatcher.toString())
.withCause(new RuntimeException(stringDescription.toString()))
.build();
} else {
viewAction.perform(uiController, targetView);
}
}
});
}
函数开始,先对viewAction
的可看性进行检查,并获取viewAction
操作对 view 限制条件 constraints
(绝大多数操作只能在相对应的控件上进行操作),然后的操作在runSynchronouslyOnUiThrea