Android列表重排UI测试难题:长按与拖拽动作无法结合实现
我最近在给我的Android应用做UI测试,这个应用里有个列表项重排的功能——用户长按列表项会生成拖拽阴影,拖到目标位置松开就能完成重排。
本来以为这个测试不难,但实际做的时候发现:单独用Barista的longClickOn("ITEM")实现长按生成拖拽阴影,或者单独写自定义Espresso ViewAction做拖拽动作都没问题,但把这两步结合成完整的长按-拖拽-松开流程就搞不定了,试了好多次都失败。
最初尝试的自定义拖拽ViewAction
我一开始写的拖拽动作是这样的:
return new ViewAction() { @Override public Matcher<View> getConstraints() { return isAssignableFrom(ViewGroup.class); } @Override public String getDescription() { return "Swiping child " + srcIndex + " to child " + destIndex; } @Override public void perform(UiController uiController, View view) { ViewGroup parent = (ViewGroup) view; final View srcChild = parent.getChildAt(srcIndex); final View destChild = parent.getChildAt(destIndex); final CoordinatesProvider srcCoordinatesProvider = new CoordinatesProvider() { @Override public float[] calculateCoordinates(View view) { int[] location = new int[2]; srcChild.getLocationInWindow(location); float x = location[0] + (view.getMeasuredWidth() / 2); float y = location[1] + (view.getMeasuredHeight() / 2); return new float[] {x, y}; } }; final CoordinatesProvider destCoordinatesProvider = new CoordinatesProvider() { @Override public float[] calculateCoordinates(View view) { int[] location = new int[2]; destChild.getLocationInWindow(location); float x = location[0] + (view.getMeasuredWidth() / 2); float y = location[1] + (view.getMeasuredHeight() / 2); return new float[] {x, y}; } }; GeneralSwipeAction swipe = new GeneralSwipeAction(Swipe.FAST, srcCoordinatesProvider, destCoordinatesProvider, Press.FINGER); swipe.perform(uiController, parent); } };
参考改进后的代码
后来参考了@Be_Negative的回答,我重写了拖拽的ViewAction,把长按和拖拽整合到了一起:
private static ViewAction drag(final int srcIndex, final int destIndex) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isAssignableFrom(ViewGroup.class); } @Override public String getDescription() { return "Swiping child " + srcIndex + " to child " + destIndex; } @Override public void perform(UiController uiController, View view) { ViewGroup parent = (ViewGroup) view; uiController.loopMainThreadUntilIdle(); final View srcChild = parent.getChildAt(srcIndex); final View destChild = parent.getChildAt(destIndex); final CoordinatesProvider coordinatesProvider = getCoordinatesProdvider(); float[] precision = Press.PINPOINT.describePrecision(); MotionEvent downEvent = MotionEvents.sendDown(uiController, coordinatesProvider.calculateCoordinates(srcChild), precision).down; try { long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5); uiController.loopMainThreadForAtLeast(longPressTimeout); float[][] steps = interpolateDragging( coordinatesProvider.calculateCoordinates(srcChild), coordinatesProvider.calculateCoordinates(destChild) ); uiController.loopMainThreadUntilIdle(); for(float[] step : steps) { if( !MotionEvents.sendMovement(uiController, downEvent, step)) { MotionEvents.sendCancel(uiController, downEvent); } } if(!MotionEvents.sendUp(uiController, downEvent, coordinatesProvider.calculateCoordinates(destChild))) { MotionEvents.sendCancel(uiController, downEvent); } } catch(Exception e) { System.out.println(e); } finally { downEvent.recycle(); } } }; }
(注:这里的getCoordinatesProdvider()和interpolateDragging()是我自己实现的辅助方法,用来获取视图中心坐标和生成拖拽的路径步骤)
现在遇到的错误
结果运行测试的时候,在MotionEvents.sendMovement这一步抛出了错误:
Error performing 'inject motion event (corresponding down event: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=540.0, y[0]=302.0, toolType[0]=TOOL_TYPE_UNKNOWN, buttonState=BUTTON_PRIMARY, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=34586525, downTime=34586525, deviceId=0, source=0x1002 })' on view 'unknown'.
有没有大佬能帮我看看这问题出在哪,怎么才能顺利实现完整的长按拖拽重排的UI测试流程?
内容的提问来源于stack exchange,提问作者Hopeful Llama

