(4.5.5.3)Espresso的进阶: ViewAction
来源:互联网 发布:c语言线程同步 编辑:程序博客网 时间:2024/06/07 02:48
- ViewAction
- 1 源码分析
- 11 所有的动作都 实现 interface ViewAction
- 10 ViewActions 相当于工具类提供一些公用的方法
- 2 系统支持
- 21 Click Press 类型
- 211 click点击
- 212 doubleclick双击
- 213 longclick长按
- 215 pressImeActionButton
- 216 pressKeyEspressoKey key pressKeyint keyCode 物理按键
- 217 pressMenuKey菜单键 点击
- 214 pressBack后退键 点击
- 218 closeSoftKeyboard关闭键盘
- 219 openLink 链接点击
- 22 Text类型
- 221 typeText 获得焦点并注入文本
- 222 typeTextIntoFocusedView在已获得焦点的View上注入文本
- 223 clearText 文本清除
- 224 replaceText String 文本替换
- 23 Gestures类型
- 231 scrollTo 滑动到指定View
- 232 swipeXX 向XX方向滑动
- 21 Click Press 类型
- RecyleView
- 1 源码分析
Espresso编写自动化就做三件事情:找某些东西、做某些事情、检查某些东西
- 找到 并返回 XXInteraction交互类
- public static ViewInteraction onView(final Matcher< View > viewMatcher)
- public static DataInteraction onData(Matcher< ? extends Object> dataMatcher)
- 执行相关动作 ,返回 XXInteraction交互类
- ViewInteraction perform(final ViewAction… viewActions)
- 校验,返回 XXInteraction交互类
- ViewInteraction check(final ViewAssertion viewAssert)
ViewAction
2.1 源码分析
2.1.1 所有的动作都 实现 interface ViewAction
/** * 识别所操作的对象类型 * http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html */ public Matcher<View> getConstraints(); //视图操作的一个描述 public String getDescription(); /** * 实际的一个操作,在之类我们就可以获取到操作的对象了 * * @param uiController the controller to use to interact with the UI. * @param view the view to act upon. never null. */ public void perform(UiController uiController, View view);
- 一个简单的示例:获取VTextView的文本
//获取指定view的文本内容String tv = getText(withId(R.id.textToBeChanged));public String getText(final Matcher<View> matcher) { final String[] text = {null}; onView(matcher).perform(new ViewAction() { //识别所操作的对象类型 @Override public Matcher<View> getConstraints() { return isAssignableFrom(TextView.class); } //视图操作的一个描述 @Override public String getDescription() { return "getting text from a TextView"; } //实际的一个操作,在之类我们就可以获取到操作的对象了。 @Override public void perform(UiController uiController, View view) { TextView textView = (TextView)view; text[0] = textView.getText().toString(); } }); return text[0]; }
2.1.0 ViewActions 相当于工具类,提供一些公用的方法
/** * A collection of common {@link ViewActions}.一组公共ViewAction的集合 */public final class ViewActions { private ViewActions() {} /** * “滑动”操作的距离,相对于View控件的长度 */ private static final float EDGE_FUZZ_FACTOR = 0.083f; /** * A.1 存储了一系列的 校验规则,在执行相关ViewActions前,先执行校验 */ private static Set<Pair<String, ViewAssertion>> globalAssertions = new CopyOnWriteArraySet<Pair<String, ViewAssertion>>(); /** * A.1.1 加入一个 “动作执行前的校验” */ public static void addGlobalAssertion(String name, ViewAssertion viewAssertion) { checkNotNull(name); checkNotNull(viewAssertion); Pair<String, ViewAssertion> vaPair = new Pair<String, ViewAssertion>(name, viewAssertion); checkArgument(!globalAssertions.contains(vaPair), "ViewAssertion with name %s is already in the global assertions!", name); globalAssertions.add(vaPair); } /** * A.1.2 移除一个“动作执行前的校验” */ public static void removeGlobalAssertion(ViewAssertion viewAssertion) { boolean removed = false; for (Pair<String, ViewAssertion> vaPair : globalAssertions) { if (viewAssertion != null && viewAssertion.equals(vaPair.second)) { removed = removed || globalAssertions.remove(vaPair); } } checkArgument(removed, "ViewAssertion was not in global assertions!"); } // A.1.3 清空“动作执行前的校验” public static void clearGlobalAssertions() { globalAssertions.clear(); } /** * B.1 在执行指定的ViewAction 之前,先进行相关的校验; * 实质上就是对 “指定的ViewAction”进行了封装,加入了校验 */ public static ViewAction actionWithAssertions(final ViewAction viewAction) { if (globalAssertions.isEmpty()) { return viewAction; } return new ViewAction() { @Override public String getDescription() { StringBuilder msg = new StringBuilder("Running view assertions["); for (Pair<String, ViewAssertion> vaPair : globalAssertions) { msg.append(vaPair.first); msg.append(", "); } msg.append("] and then running: "); msg.append(viewAction.getDescription()); return msg.toString(); } @Override public Matcher<View> getConstraints() { return viewAction.getConstraints(); } @Override public void perform(UiController uic, View view) { for (Pair<String, ViewAssertion> vaPair : globalAssertions) { Log.i("ViewAssertion", "Asserting " + vaPair.first); vaPair.second.check(view, null); } viewAction.perform(uic, view); } }; } ...
2.2 系统支持
2.2.1 Click | Press 类型
2.2.1.1 click()点击
/** * 点击 * View必须显示在屏幕上 */ public static ViewAction click() { return actionWithAssertions( new GeneralClickAction(Tap.SINGLE, GeneralLocation.VISIBLE_CENTER, Press.FINGER)); } /** * 单击 * * If the click takes longer than the 'long press' duration (which is possible) the provided * rollback action is invoked on the view and a click is attempted again. * * This is only necessary if the view being clicked on has some different behaviour for long press * versus a normal tap. * * For example - if a long press on a particular view element opens a popup menu - * ViewActions.pressBack() may be an acceptable rollback action. * * <br> * View constraints: * <ul> * <li>must be displayed on screen</li> * <li>any constraints of the rollbackAction</li> * <ul> */ public static ViewAction click(ViewAction rollbackAction) { checkNotNull(rollbackAction); return actionWithAssertions( new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, rollbackAction)); }
2.2.1.2 doubleclick()双击
/** * 双击 * 要求:view在界面上 */ public static ViewAction doubleClick() { return actionWithAssertions( new GeneralClickAction(Tap.DOUBLE, GeneralLocation.CENTER, Press.FINGER)); }
2.2.1.3 longclick()长按
/** * 长按 * 要求:view在界面上 */ public static ViewAction longClick() { return actionWithAssertions( new GeneralClickAction(Tap.LONG, GeneralLocation.CENTER, Press.FINGER)); }
2.2.1.5 pressImeActionButton()
/** * Returns an action that presses the current action button (next, done, search, etc) on the IME * (Input Method Editor). The selected view will have its onEditorAction method called. */ public static ViewAction pressImeActionButton() { return actionWithAssertions(new EditorAction()); }
2.2.1.6 pressKey(EspressoKey key) & pressKey(int keyCode) 物理按键
2.2.1.7 pressMenuKey()菜单键 点击
2.2.1.4 pressBack()后退键 点击
// 后退键 点击 public static ViewAction pressBack() { return pressKey(KeyEvent.KEYCODE_BACK); } // 菜单键 点击 public static ViewAction pressMenuKey() { return pressKey(KeyEvent.KEYCODE_MENU); } /** * Returns an action that presses the specified key with the specified modifiers. */ public static ViewAction pressKey(EspressoKey key) { return actionWithAssertions(new KeyEventAction(key)); } /** * Returns an action that presses the key specified by the keyCode (eg. Keyevent.KEYCODE_BACK). */ public static ViewAction pressKey(int keyCode) { return actionWithAssertions( new KeyEventAction(new EspressoKey.Builder().withKeyCode(keyCode).build())); }
2.2.1.8 closeSoftKeyboard()关闭键盘
/** * 关闭键盘 * 如果键盘是关闭状态则不处理 */ public static ViewAction closeSoftKeyboard() { return actionWithAssertions(new CloseKeyboardAction()); }
2.2.1.9 openLink 链接点击
public static ViewAction openLinkWithText(String linkText) { return openLinkWithText(is(linkText)); } public static ViewAction openLinkWithUri(String uri) { return openLinkWithUri(is(Uri.parse(uri))); } public static ViewAction openLinkWithText(Matcher<String> linkTextMatcher) { return openLink(linkTextMatcher, any(Uri.class)); } public static ViewAction openLinkWithUri(Matcher<Uri> uriMatcher) { return openLink(any(String.class), uriMatcher); } /** * 打开连接,支持Uri 和 text格式的. 通过调用 连接的onclick方法. * 需要: * <li>已经在屏幕上显示 * <li>是TextView的子类 * <li>含有超链接 */ public static ViewAction openLink(Matcher<String> linkTextMatcher, Matcher<Uri> uriMatcher) { checkNotNull(linkTextMatcher); checkNotNull(uriMatcher); return actionWithAssertions(new OpenLinkAction(linkTextMatcher, uriMatcher)); }}
2.2.2 Text类型
2.2.2.1 typeText() 获得焦点并注入文本
2.2.2.2 typeTextIntoFocusedView()在已获得焦点的View上注入文本
/** * 先点击以选中View,然后一个一个的键入指定的文本字符,使用了ENTER key event. * 注意:这个行为将先执行一个tap动作以获取焦点,如果之前View里就有文本,焦点可能在任意位置 * View 条件: * <li>已经显示在屏幕上 * <li>支持输入 * <ul> */ public static ViewAction typeText(String stringToBeTyped) { return actionWithAssertions(new TypeTextAction(stringToBeTyped)); } /** * 在 已获得焦点的 view中一个一个键入输入的文本 * 注意:是已获得焦点的View,有可能不是当前指定的View * View 需要满足 * <li>已经在屏幕上 * <li>支持输入 * <li>已经获得了焦点 * <ul> */ public static ViewAction typeTextIntoFocusedView(String stringToBeTyped) { return actionWithAssertions(new TypeTextAction(stringToBeTyped, false /* tapToFocus */)); }/** * Enables typing text on views. */public final class TypeTextAction implements ViewAction { private static final String TAG = TypeTextAction.class.getSimpleName(); private final String stringToBeTyped; private final boolean tapToFocus; /** * Constructs {@link TypeTextAction} with given string. If the string is empty it results in no-op * (nothing is typed). By default this action sends a tap event to the center of the view to * attain focus before typing. * * @param stringToBeTyped String To be typed by {@link TypeTextAction} */ public TypeTextAction(String stringToBeTyped) { this(stringToBeTyped, true); } /** * Constructs {@link TypeTextAction} with given string. If the string is empty it results in no-op * (nothing is typed). * * @param stringToBeTyped String To be typed by {@link TypeTextAction} * @param tapToFocus indicates whether a tap should be sent to the underlying view before typing. */ public TypeTextAction(String stringToBeTyped, boolean tapToFocus) { checkNotNull(stringToBeTyped); this.stringToBeTyped = stringToBeTyped; this.tapToFocus = tapToFocus; } @SuppressWarnings("unchecked") @Override public Matcher<View> getConstraints() { Matcher<View> matchers = allOf(isDisplayed()); if (!tapToFocus) { matchers = allOf(matchers, hasFocus()); } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return allOf(matchers, supportsInputMethods()); } else { // SearchView does not support input methods itself (rather it delegates to an internal text // view for input). return allOf(matchers, anyOf(supportsInputMethods(), isAssignableFrom(SearchView.class))); } } @Override public void perform(UiController uiController, View view) { // No-op if string is empty. if (stringToBeTyped.length() == 0) { Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed)."); return; } if (tapToFocus) { // Perform a click. new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER) .perform(uiController, view); uiController.loopMainThreadUntilIdle(); } try { if (!uiController.injectString(stringToBeTyped)) { Log.e(TAG, "Failed to type text: " + stringToBeTyped); throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException("Failed to type text: " + stringToBeTyped)) .build(); } } catch (InjectEventSecurityException e) { Log.e(TAG, "Failed to type text: " + stringToBeTyped); throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(e) .build(); } } @Override public String getDescription() { return String.format("type text(%s)", stringToBeTyped); }}
2.2.2.3 clearText() 文本清除
2.2.2.4 replaceText( String ) 文本替换
/** * 清除文本 * View必须满足: * <li>已经显示在屏幕上 * <li>是EditText及其子类 */ public static ViewAction clearText() { return actionWithAssertions(new ReplaceTextAction("")); } /** * 一次性的将View的文本更新为指定文本 * View必须满足: * <li>已经显示在屏幕上 * <li>是EditText及其子类 */ public static ViewAction replaceText(@Nonnull String stringToBeSet) { return actionWithAssertions(new ReplaceTextAction(stringToBeSet)); }/** * 通过view.setText()修改文本 */public final class ReplaceTextAction implements ViewAction { private final String stringToBeSet; public ReplaceTextAction(String value) { checkNotNull(value); this.stringToBeSet = value; } @SuppressWarnings("unchecked") @Override public Matcher<View> getConstraints() { return allOf(isDisplayed(), isAssignableFrom(EditText.class)); } @Override public void perform(UiController uiController, View view) { ((EditText) view).setText(stringToBeSet); } @Override public String getDescription() { return "replace text"; }}
2.2.3 Gestures类型
2.2.3.1 scrollTo() 滑动到指定View
/** * 滑动到指定View * 需要: * <li>是 在 ScrollView 的内部;注意listview等不符合要求 * <li>visibility == View.VISIBLE */ public static ViewAction scrollTo() { return actionWithAssertions(new ScrollToAction()); }
2.2.3.2 swipeXX() 向XX方向滑动
/** * 从垂直方向的中间,从右向左滑动. * 不见得是从边缘滑动,而是有一定的偏差 * <br> * 需要 * <li>必须已经显示在屏幕上 */ public static ViewAction swipeLeft() { return actionWithAssertions(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.translate(GeneralLocation.CENTER_RIGHT, -EDGE_FUZZ_FACTOR, 0), GeneralLocation.CENTER_LEFT, Press.FINGER)); } /** * 从垂直方向的中间,从左向右滑动. * 不见得是从边缘滑动,而是有一定的偏差 * 需要 * <li>必须已经显示在屏幕上 */ public static ViewAction swipeRight() { return actionWithAssertions(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.translate(GeneralLocation.CENTER_LEFT, EDGE_FUZZ_FACTOR, 0), GeneralLocation.CENTER_RIGHT, Press.FINGER)); } /** * 从水平方向的中间,从上向下滑动. * 不见得是从边缘滑动,而是有一定的偏差 * 需要 * <li>必须已经显示在屏幕上 */ public static ViewAction swipeDown() { return actionWithAssertions(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.translate(GeneralLocation.TOP_CENTER, 0, EDGE_FUZZ_FACTOR), GeneralLocation.BOTTOM_CENTER, Press.FINGER)); } /** * 从水平方向的中间,从下向上滑动. * 不见得是从边缘滑动,而是有一定的偏差 * 需要 * <li>必须已经显示在屏幕上 */ public static ViewAction swipeUp() { return actionWithAssertions(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.translate(GeneralLocation.BOTTOM_CENTER, 0, -EDGE_FUZZ_FACTOR), GeneralLocation.TOP_CENTER, Press.FINGER)); }
RecyleView
RecyclerViews 和 AdapterViews 的工作原理不同,因此onData()不适用于RecyleView
如果想要与RecyleView交互,请引入“espresso-contrib”,里边包含一系列的Actions可以用于滚动和点击
- scrollTo - Scrolls to the matched View.
- scrollToHolder - Scrolls to the matched View Holder.
- scrollToPosition - Scrolls to a specific position.
- actionOnHolderItem - Performs a View Action on a matched View Holder.
- actionOnItem - Performs a View Action on a matched View.
- actionOnItemAtPosition - Performs a ViewAction on a view at a specific position.
示例:
public static class RecyclerViewActions{ /** * 根据 子view中包含的文本----滚到 且 单击 * onView(withId(R.id.recycler_main)).perform(clickWithChildDescendant("销售机会")); * @param childHasText 子view中包含的文本 * @return */ public static ViewAction clickWithChildDescendant(String childHasText){ return actionOnItem(hasDescendant(withText(childHasText)), click()); } /** * 根据 子view中包含的文本----滚到 且 单击 * recyclerView.perform(actionOnItemAtPosition(10, click())); * @param childPos 子view中包含的文本 * @return */ public static ViewAction clickWithChildPos(int childPos){ return actionOnItemAtPosition(childPos, click()); } }
再者:
onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition(4,click()));onView(withId(R.id.recyclerView)) .perform(RecyclerViewActions.actionOnItem( hasDescendant(withText("Effective Java ")), click()));
0 0
- (4.5.5.3)Espresso的进阶: ViewAction
- (4.5.5.5)Espresso的进阶: ViewAssertions
- (4.5.5.6)Espresso的进阶: IdlingResource
- (4.5.5.4)Espresso的进阶: AdapterViewProtocol
- (4.5.5.4)Espresso的进阶: OnView & onData & Matchers
- (4.5.5.9)Espresso之UiAutomator2与Espresso的结合
- (4.5.5.10)Espresso之Robotium与Espresso的结合
- (4.5.5.2)Espresso的基础
- Espresso自动化测试(十三)- UiAutomator2与Espresso的结合
- (4.5.5.1) Espresso的简介、下载和安装
- 大牛们的浓咖啡(Espresso)简单介绍
- (4.5.5.7)Espresso之Intent测试
- Espresso指南二(Espresso意图)
- Espresso
- Espresso 自动化测试(四)-中文字符的输入
- Espresso 自动化测试(五)- onData() 的使用
- Espresso 自动化测试(八)- SeekBar的测试
- Espresso 不依赖源代码的测试
- 搜狗输入法正常使用一段时间后 突然无法使用
- c++ primer plus复合类型之数组的代替品
- split对象为空报错!
- php中用到的函数(个人用到的)
- 基督教给予我们什么
- (4.5.5.3)Espresso的进阶: ViewAction
- 提高mysql千万级大数据SQL查询优化30条经验
- POJ 1925 Spiderman (DP)
- Macaca 实现 iosMonkey (python 版)
- Mysql的表的碎片清理
- eclipse中svn插件替换用户名密码,解决总是提示输入密码 问题
- OLT的配置过程
- TensorFlow 图像数据预处理及可视化
- 计算中文字符串长度