• UiAutomator2.0(转)


    1.     概述

    UI测试(功能测试、黑盒测试)不需要测试者了解应用程序的内部实现细节,只需要知道当执行了某些特定的动作后是否会得到其预期的输出。这种测试方法,在团队合作中可以更好地分离的开发和测试角色。

    常见的UI测试的方法是手动去执行,然后去验证程序是否达到的预期的效果,很显然这种方法耗时、繁琐并且很容易出错。因此我们需要一种可靠的方法来进行UI测试,通过测试框架,我们可以完成针对具体使用场景的测试用例,然后可以循环的、自动的来运行我们的测试case。

    Android的SDk提供了以下的工具来支持我们进行UI自动化测试:

    uiautomatorviewer:一个用来扫描和分析android应用程序的UI控件的GUI工具。

    uiautomator:一个包含创建测试、执行自动化测试API的Java库。(Uiautomator文档:http://android.toolib.NET/tools/help/uiautomator/index.html )

    要使用这些工具,你必须安装Android开发工具以下版本:

    Android SDKTools:API 21 版本或者21以上版本;

    Android SDKPlatform:API 16 版本或者16以上版本.

    2.     UiAutomatorViewer使用

    在你开始写测试用例之前,使用uiautomatorviewer可以帮助你熟悉你的UI组件(包括视图和控件)。你可以使用它对当前连接到你电脑上的手机屏幕进行一个快照,然后可以看到手机当前页面的层级关系和每个控件的属性。利用这些信息,你可以写出针对特定UI控件的测试用例。

    在 ..sdk ools 目录下打开uiautomatorviewer.bat (打开前请手机连接电脑)

    1)        获取快照

    当你要分析一个页面时,首先将手机的页面停留在你要分析的页面,然后用数据线连接电脑。然后点击uiautomatorviewer左上角的第二个图标按钮 Device Screenshot,点击之后会将当前手机界面的快照更新到这里来。

    2)        页面层级

    右上方的整个区域,就是当前页面布局的层级关系。

    3)        控件属性

    右下方的整个区域,是当前选中的页面或者是控件的属性信息。这部分比较重要,我们以后写代码的时候就是需要通过查看属性中的控件的id或者是text等来获取控件的实例,然后点击操作它。

     

    我们可以通过text、resource-id、class、content-desc等来获取控件。

    3.     UiAutomator

    UiAutomator2.0做了一些改进:

    1)      基于 Instrumentation,可以获取应用 Context,使用 Android 服务及接口

    2)      基于 Junit 4,测试用例无需继承于任何父类,方法名不限,使用注解 Annotation 进行

    UI 执行效率比 1.0 快,测试执行可使用Android Junit 方式及 gradle 方式

    3)      API 更新,新增UiObject2、Until、By、BySelector 等:API For UI Automator

    4)      Log 输出变更,以往使用System.out.print 输出流回显至执行端,2.0 输出至 Logcat。

    4.     UiAutomator2.0使用步骤

    1)        在android studio新建一个工程

    2)        添加依赖

     

    [java] view plain copy
     
    1. androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {  
    2.     exclude group:'com.android.support',module:'support-annotations'  
    3. })  
    4. testCompile 'junit:junit:4.12'  
    5. // Set this dependencyto build and run UI Automator tests  
    6. androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'  



    3)        在androidTest目录下新建一个Test, 点击button4,跳转到一个新页面

    @RunWith(AndroidJUnit4.class)
    @SdkSuppress(minSdkVersion = 18)
    public class Test1 {
        private UiDevice mDevice;
    
        @Before
        public void before() {
            // Initialize UiDevice instance
            mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
            assertThat(mDevice, notNullValue());
    
            // Start from the home screen
            mDevice.pressHome();
            // open app
            openApp("com.ut.anquanguankong");
        }
    
        @Test
        public void test() throws InterruptedException {
            //点击desc=button4的按钮
            findObject(By.desc("button4")).click();
        }
    
        public void openApp(String packageName) {
            Context context = InstrumentationRegistry.getInstrumentation().getContext();
            Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            context.startActivity(intent);
        }
    
        public UiObject2 findObject(BySelector selector) throws InterruptedException {
            UiObject2 object = null;
            int timeout = 30000;
            int delay = 1000;
            long time = System.currentTimeMillis();
            while (object == null) {
                object = mDevice.findObject(selector);
                sleep(delay);
                if (System.currentTimeMillis() - timeout > time) {
                    break;
                }
            }
            return object;
        }
    }

    5.     UiAutomator2.0 API

    UiAutomator2.0是兼用1.0的,2.0的API会包含1.0的API。通过了解这API方法,就可以编写UI自动化测试代码了。官方文档:

    https://developer.android.google.cn/reference/android/support/test/uiautomator/package-summary.html,下面介绍常用的2.0 API:

     

    1)   InstrumentationRegistry

    a)        类说明

    一个暴露的注册实例,持有instrumentation运行的进程和参数,还提供了一种简便的方法调用instrumentation, application context和instrumentation参数。

    b)        相关API

    返回类型

    API

    static Bundle

    getArguments(): 返回一个instrumentation参数副本

    static Context

    getContext():  返回instrumentation对应包的Context

    InstrumentationRegistry.getContext() == instrumentation.getContext()

    static Instrumentation

    getInstrumentation(): 返回当前运行的instrumentation

    static Context

    getTargetContext(): 返回一个目标应用程序的Context

    static void

    registerInstance(Instrumentation instrumentation, Bundle arguments):记录或暴露当前instrumentation运行和instrumentation参数包的副本,存储在注册中心

    c)        示例

    @Test
    public void InstrumentationRegistryTest() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Context context1 = InstrumentationRegistry.getContext();
        Context context2 = InstrumentationRegistry.getTargetContext();
        Context context3= instrumentation.getContext();
    
        if(context1 == context2) {
            Log.i("Chris",  "InstrumentationRegistry getContext == getTargetContext");
        }else {
            Log.i("Chris",  "InstrumentationRegistry getContext != getTargetContext");
        }
    
        if(context1 == context3) {
            Log.i("Chris",  "InstrumentationRegistry getContext == Instrumentation getContext");
        }else {
            Log.i("Chris",  "InstrumentationRegistry getContext != Instrumentation getContext");
        }
    
        Intent intent = context2.getPackageManager().getLaunchIntentForPackage("xxx.xxx.xxx");
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context2.startActivity(intent);
    }

    2)   UiDevice

    a)        类说明

    UiDevice用与访问关设备状态的信息,也可以使用这个类来模拟用户在设备上的操作。可以通过下面的方法得到实例:

    UiDevice mdevice = getUiDevice();

    b)        相关API

    返回类型

    API

    Boolean

    click(int x, int y): 模拟用户在指定位置点击

    String

    getCurrentActivityName():  获得的是应用程序在桌面上显示的名字

    String

    getCurrentPackageName():获得当前显示的应用程序的包名

    int

    getDisplayHeight():获得当前设备的屏幕分辨率的高

    int

    getDisplayWighth():获得当前设备的屏幕分辨率的宽

    boolean

    isScreenOn():判断手机当前是否灭屏

    Void

    wakeUp():点亮当前屏幕

    Boolean

    pressBack():点击back键

    Boolean

    pressHome():点击home键

    Boolean

    PressMenu():点击menu键

    Boolean

    PressCode(int code): 利用keycode值模拟一次按下事件, 例如,需要按下数字1 数字1的keycode是 KEYCODE_NUMPAD_1,更多keycode可以在 http://developer.android.com/intl/zh-cn/reference/android/view/KeyEvent.html 进行查询

    boolean

    swipe(int startX, int startY, int endX, int endY, int steps): 用指定的步长,从A点滑动B点

    boolean

    takeScreenshot(File storePath): 截取当前屏幕,保存到文件

    UiAutomator2在UiDevice新增的API

    返回类型

    API

    void

    dumpWindowHierarchy(OutPutStream out): 获取当前页面层级到输出流

    String

    executeShellCommand(String cmd): 执行一个shell命令。备注:此方法只支持api21以上,手机需要5.0系统以上

    UiObject2

    findObject(BySelector selector): 返回第一个匹配条件的对象

    UiObject

    findObject(UiSelector selector): 返回一个匹配条件的代表视图的UiObject对象

    List<UiObject2>

    findObjects(BySelector selector): 返回所有匹配条件的对象

    <R> R

    wait(SearchCondition<R> condition, long timeout): 等待的条件得到满足


    3)   BySelector和By

    a)        类说明

    BySelector和By是UiAutomator2.0的类。

    BySelector类为指定搜索条件进行匹配UI元素,通UiDevice.findObject(BySelector)方式进行使用。

    By类是一个实用程序类,可以以简洁的方式创建BySelectors对象。主要功能是使用缩短语法,提供静态工厂方法来构造BySelectors对象。例如:你将使用findObject(By.text("foo")),而不是findObject(newSelector().text("foo"))的方式来查找文本值为“foo”的UI元素。

    b)        相关API

    在这里介绍By的API,BySelector的API和By的对应的。

    返回类型

    API

    BySelector

    clazz(String calssName),

    clazz(String packageName, String className),

    clazz(Class clazz),

    clazz(Pattern className)

    通过class来匹配UI

    BySelector

    desc(String contentDescription)

    descContains(String substring)

    descStartsWith(String substring)

    descEndsWith(String substring)

    desc(Pattern contentDescription)

    通过contentDescription来匹配UI

    BySelector

    text(String contentDescription)

    textContains(String substring)

    textStartsWith(String substring)

    textEndsWith(String substring)

    text(Pattern contentDescription)

    通过text来匹配UI

    BySelector

    res(String resourceName)

    res(String resourcePackage, String resourceId)

    res(Pattern resourceName)

    通过id来匹配UI

    BySelector

    checkable(boolean isCheckable)

    BySelector

    clickable(boolean isClickable)

    BySelector

    enabled(boolean isEnabled)

    BySelector

    focusable(boolean isFocusable)

    BySelector

    focused(boolean isFocused)

    BySelector

    longClickable(boolean isLongClickable)

    BySelector

    scrollable(boolean isScrollable)

    4)   UiObject2

    1)        类说明

    可以理解为直接操作界面ui元素的实例。UiObject2是UiAutomator2的类。

    2)        相关API

    基本动作

    API

    说明

    clear()

    清楚编辑框内的内容

    click()

    点击一个对象

    clickAndWait(EventCondition<R> condition, long timeout)

    点击一个对象然后等待在超时的时间内条件满足则通过,否则抛出异常

    drag(Point dest, int speed)

    自定义速度拖拽这个对象到指定位置

    drag(Point dest)

    拖拽这个对象到指定位置

    longClick()

    长按某个对象

    scroll(Direction direction, float percent)

    对该对象执行一个滚动操作

    scroll(Direction direction, float percent, int speed)

    自定义速度,对该对象执行一个滚动操作

    setText(String text)

    设置文本内容

    legacySetText(String text)

    通过发送keycode,设置文本内容

    手势动作

    API

    说明

    pinchClose(float percent, int speed)

    自定义速度执行收缩手势

    pinchClose(float percent)

    执行收缩手势

    pinchOpen(float percent, int speed)

    自定义速度执行展开手势

    pinchOpen(float percent)

    执行展开手势

    fling(Direction direction)

    执行一个扫动手势,Direction代表为起点方向

    fling(Direction direction, int speed)

    自定义速度,执行一个扫动手势

    swipe(Direction direction, float percent, int speed)

    执行一个滑动操作,可自定义滑动距离和速度

    swipe(Direction direction, float percent)

    执行一个滑动操作

    setGestureMargin(int margin)

    以像素为单位,设置手势边缘

    setGestureMargins(int left, int top, int right, int bottom)

    以像素为单位,设置手势边缘

    获取层级与条件判断

    API

    说明

    findObject(BySelector selector)

    搜索在这个对象之下的所有元素,并返回第一个与搜索条件匹配的

    findObjects(BySelector selector)

    搜索在这个对象之下的所有元素,并返回所有与搜索条件匹配的

    getChildCount()

    返回这个对象直属子元素的数量

    getChildren()

    返回这个对象下的直接子元素的集合

    getParent()

    返回该对象的父类

    equals(Object object)

    比较两个对象是否相等

    hashCode()

    获取对象的哈希码

    hasObject(BySelector selector)

    返回该对象是否存在

    recycle()

    回收该对象

    wait(UiObject2Condition<R> condition, long timeout)

    等待条件被满足

    wait(SearchCondition<R> condition, long timeout)

    等待条件被满足

    5)   Configration

    a)        类说明

    Configrator用于设置脚本动作的默认延时:

    1.        可调节两个模拟动作之间的默认间隔

    2.        可调节输入文本的输入时间间隔

    3.        可调节每次滚动的时间间隔

    4.        可调节等待系统空闲的默认时间

    b)        相关API

    延时项

    默认延时

    说明

    API

    动作

    3s

    设置延时

    setActionAcknowledgmentTimeout(long timeout)

    获取默认延时

    getActionAcknowledgmentTimeout()

    键盘输入

    0s

    设置延时

    setKeyInjectionDelay(long delay)

    获取默认延时

    getKeyInjectionDelay()

    滚动

    200ms

    设置延时

    setScrollAcknowledgmentTimeout(long timeout)

    获取默认延时

    getScrollAcknowledgmentTimeout()

    空闲

    10s

    设置延时

    setWaitForIdleTimeout(long timeout)

    获取默认延时

    getWaitForIdleTimeout()

    组件查找

    10s

    设置延时

    setWaitForSelectorTimeout(long timeout)

    获取默认延时

    getWaitForSelectorTimeout()

    6.     断言

    1)   断言函数介绍

    确定被测试的方法是否按照预期的效果正常工作

    比如说:

    if (假设成立){

        通过测试

    }else{

        报错并终止当前用例测试

    }

    2)   断言函数用例结构

    一个完整的测试用例必需要有断言函数

    setUp//初始化

    //测试用例,junit4版本才可以使用多条用例

    test        初始化场景与数据

    test        模拟操作步骤

    test        断言

    test        恢复场景   

    tearDown//回收初始化垃圾

    3)   断言函数Java错误类型

    a)        Error:

    一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类错误导致的应用程序中断,仅靠程序本身无法恢复和预防(断言)

    b)        Exeeption:

    表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常(最常见的是UI对象找不到的异常)

    4)   断言函数API

    例如:

    //断言两个对象是否相等

    asserEquals(Stringmessage,Object expected,Object actual){

        if (expected==null && actual==null){

            return ;   

        }

        if (expected!=null && expected.equals(actual)){

            return

        }

        failNotEquals(message,expected,actual);

    }

    参数

    说明

    Message

    可选消息,在断言失败后会抛出这个消息

    Expected

    期望的值

    Actual

    实际的值

     

    相关API

    方法

    说明

    assertEquals(boolean,boolean)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,boolean,boolean)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(byte,byte)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,byte,byte)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(char,char)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,char,char)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(int,int)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,int,int)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(long,long)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,long,long)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(Object,Object)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,Object,Object)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(short,short)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,short,short)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,String)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(String,String,String)

    如果期望(expected)和实际(actual)相等则通过,否则失败

    assertEquals(double,double,double)

    如果期望(expected)和实际(actual)相差不超过精度值(delta)则通过,否则失败

    assertEquals(String,double,

    double,double)

    如果期望(expected)和实际(actual)相差不超过精度值(delta)则通过,否则失败

    assertEquals(float,float,float)

    如果期望(expected)和实际(actual)相差不超过精度值(delta)则通过,否则失败

    assertEquals(String,float,float,float)

    如果期望(expected)和实际(actual)相差不超过精度值(delta)则通过,否则失败

    assertFalse(boolean)

    如果条件(condition)为False则通过,否则失败

    assertFalse(String,boolean)

    如果条件(condition)为False则通过,否则失败

    assertTrue(boolran)

    如果条件(condition)为True则通过,否则失败

    assertTrue(String,boolran)

    如果条件(condition)为True则通过,否则失败

    assertNotNull(Object)

    如果条件(condition)为非空则通过,否则失败

    assertNotNull(String,Object)

    如果条件(condition)为非空则通过,否则失败

    assertNull(Object)

    如果条件(condition)为空则通过,否则失败

    assertNull(String,Object)

    如果条件(condition)为空则通过,否则失败

    assertNotSame(Object,object)

    如果期望(expected)和实际(actual)引用不同的内存对象对象则通过,否则失败

    assertNoteSame(String,Object,Object)

    如果期望(expected)和实际(actual)引用不同的内存对象对象则通过,否则失败

    assertSame(Object,Object)

    如果期望(expected)和实际(actual)引用相同的内存对象对象则通过,否则失败

    assertSame(String,Object,Object)

    如果期望(expected)和实际(actual)引用相同的内存对象对象则通过,否则失败

    fail()

    用例立即失败

    fail(String)

    用例立即失败,且抛出指定消息

    failNotEquals(String,Object,Object)

    用例立即失败,且抛出指定消息与期望、实际值不相等的消息

    failNotSame(String,String,String)

    用例立即失败,且抛出指定消息与期望、实际值不相等的消息

    failSame(String)

    用例立即失败,且抛出指定消息

    8.     报告分析

    1)   错误类型

    断言错误:就是断言这个用例的成功或者失败(AssrtionFailedError)

    脚本错误:UiObjectNotFoundException(找不到对象异常)、java异常等

    2)   报告分析

    @Test
     public void testMain() throws InterruptedException, UiObjectNotFoundException {
    
        BySelector tabSelector = By.desc("TabContainer");
        uiAction.click(By.desc("button4")).isExist("打开主页面出错", tabSelector);
        mDevice.pressBack();
     }

    这个方法测试:点击button4,进入主页面。

    a)        正常运行

    testMain打开一个存在的页面。

    run started: 1 tests 
    TestRunner: started: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
    InteractionController: runAndwaitForEvents timed out waiting for events
    QueryController: Got null root node from accessibility - Retrying...
    InteractionController: runAndwaitForEvents timed out waiting for events
    TestRunner: finished: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
    TestRunner: run finished: 1 tests, 0 failed, 0 ignored

    从上面报告来看,testMain正常执行。

    b)        断言错误

    testMain打开一个不存在的页面。

    run started: 1 tests
    TestRunner: started: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
    InteractionController: runAndwaitForEvents timed out waiting for events
    QueryController: Got null root node from accessibility - Retrying...
    TestRunner: failed: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    TestRunner: ----- begin exception -----
    TestRunner: junit.framework.AssertionFailedError: 打开主页面出错
    at junit.framework.Assert.fail(Assert.java:50)
    at junit.framework.Assert.assertTrue(Assert.java:20)
    at junit.framework.Assert.assertNotNull(Assert.java:218)
    at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.isExist(UiAutomatorActionImpl.java:102)
    at com.chris.example.uiautomatordemo.AnquanguankongTest.testMain(AnquanguankongTest.java:52)
    TestRunner: ----- end exception -----
    TestRunner: finished: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
    TestRunner: run finished: 1 tests, 1 failed, 0 ignored

    从上面报告来看,testMain执行失败,并给出详细的错误信息。

    c)        脚本错误

    testMain点击一个不存在的button

    run started: 1 tests
    TestRunner: started: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
    InteractionController: runAndwaitForEvents timed out waiting for events
    QueryController: Got null root node from accessibility - Retrying...
    TestRunner: failed: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    TestRunner: ----- begin exception -----
    TestRunner: junit.framework.AssertionFailedError: BySelector [DESC='Qbutton42E'] no found
    at junit.framework.Assert.fail(Assert.java:50)
    at junit.framework.Assert.assertTrue(Assert.java:20)
    at junit.framework.Assert.assertNotNull(Assert.java:218)
    at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.isExist(UiAutomatorActionImpl.java:112)
    at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.findObjectWithCheck(UiAutomatorActionImpl.java:75)
    at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.click(UiAutomatorActionImpl.java:84)
    at com.chris.example.uiautomatordemo.AnquanguankongTest.testMain(AnquanguankongTest.java:52)
    TestRunner: ----- end exception -----
    TestRunner: finished: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
    MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
    TestRunner: run finished: 1 tests, 1 failed, 0 ignored
  • 相关阅读:
    Linux 操作memcache命令行
    查看memcache版本
    磊哥测评之数据库SaaS篇:腾讯云控制台、DMC和小程序
    一看就能学会的H5视频推流方案
    JavaScript与WebAssembly进行比较
    Android调试神器stetho使用详解和改造
    5分钟入门git模式开发
    深耕品质,腾讯WeTest《2018中国移动游戏质量白皮书》正式发布
    RSA签名的PSS模式
    附实例!图解React的生命周期及执行顺序
  • 原文地址:https://www.cnblogs.com/xuegonghou/p/7407086.html
Copyright © 2020-2023  润新知