在Android P出来后,开展自动化测试的路上一些地方有些磕磕绊绊。先说说之前发现的一种必现的无法点击到桌面控件的问题,明明界面中存在,但是无法被点击,Uiautomator运行时并没有抛出这一步的异常,但是下一步就抛异常了。
详细的实例如下:case删除全部联系人的操作步骤(点击删除--选择确定--验证是否正确删除)
而因为无法点击到删除这个控件,导致没有弹出确认窗口,导致下一步的点击“确定”按钮这一步抛UiObjectNotFound异常。
@Test
public void testCase() throws UiObjectNotFoundException {
UiObject deleteBtn=new UiObject(new UiSelector().text("删除"));//未正确点击,但未抛出异常
deleteBtn.click();
UiObject okBtn=new UiObject(new UiSelector().text("确定"));//抛异常
okBtn.click();
}
那么,接下来,分析下,为什么出错了。使用UiautomatorViewer同步快照后,发现删除按钮被截取掉一部分了。
可见,控件确实存在,但是其中有一部分被干掉了,整个屏幕被切割掉了一小部分。
从Uiautomator源码中,我们可以知道,Uiautomator其实获取控件后,会获取该控件的中心点坐标,随后点击坐标,从而实现点击控件。上源码:
UiObject类的click()方法:
/**
* Performs a click at the center of the visible bounds of the UI element represented
* by this UiObject.
*
* @return true id successful else false
* @throws UiObjectNotFoundException
* @since API Level 16
*/
public boolean click() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
if(node == null) {
throw new UiObjectNotFoundException(mUiSelector.toString());
}
Rect rect = getVisibleBounds(node);
return getInteractionController().clickAndSync(rect.centerX(), rect.centerY(),
mConfig.getActionAcknowledgmentTimeout());//rect.centerX(), rect.centerY()。中心点
}
跳转到etInteractionController().clickAndSync()方法中:
/**
* Click at coordinates and blocks until either accessibility event TYPE_WINDOW_CONTENT_CHANGED
* or TYPE_VIEW_SELECTED are received.
*
* @param x
* @param y
* @param timeout waiting for event
* @return true if events are received, else false if timeout.
*/
public boolean clickAndSync(final int x, final int y, long timeout) {
String logString = String.format("clickAndSync(%d, %d)", x, y);
Log.d(LOG_TAG, logString);
return runAndWaitForEvents(clickRunnable(x, y), new WaitForAnyEventPredicate(
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED |
AccessibilityEvent.TYPE_VIEW_SELECTED), timeout) != null;
}
不确定是开发全面屏机器都这样还是部分ROM的开发者不小心留的坑,但,无力吐槽,更无力修复这种软件状态,只能寻找解决方案。
多方尝试后,发现adb shell input tap <x> <y> 可以实现屏幕点击,而滑动(长按)则可以通过adb shell input swipe <x1> <y1> <x2> <y2> [duration(ms)]来实现。而且,上面的切割部分的坐标,也是可以点击到的。辣么,解决方案便出来了。只要修改下点击的方式更换为Runtime.getRuntime().exec("input tap "+rect.centerX()+" "+ rect.centerY()),可以是在源码上做修改,也可以是在自己二次封装的代码上替换掉就可以了。贴上adb shell input 帮助文档:
C:\Users>adb shell input
Usage: input [<source>] <command> [<arg>...]
The sources are:
dpad
keyboard
mouse
touchpad
gamepad
touchnavigation
joystick
touchscreen
stylus
trackball
The commands and default sources are:
text <string> (Default: touchscreen)
keyevent [--longpress] <key code number or name> ... (Default: keyboard)
tap <x> <y> (Default: touchscreen)
swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
press (Default: trackball)
roll <dx> <dy> (Default: trackball)