一、一个BUG引发的问题


    如果研发过程中有一个BUG:“不断的切换手机语言出现花屏现象”。这个问题我们如何验证呢?我想,最好的方式应该是自动化测试。


    那么,自动化测试可以完成哪些任务呢?


    简单的说,那些重复性的测试工作,都可以交给自动化完成:


        1、设置手机的语言
        2、添加、删除、收藏联系人
        3、拨号、挂断
        4、甚至发送短信、收藏短信


    如果需要上面的功能,那么就开始自动化之旅吧。


二、Android自动化测试简单介绍


    Android自动化测试主要分为Monkeyrunner、Rubotium、UiAutomator、Monkey(在我看来这个不算)等。主要特点:


   

1、Monkeyrunner:优点:操作最为简单,可以录制测试脚本,可视化操作;缺点:主要生成坐标的自动化操作,移植性不强,功能最为局限;


   

2、Rubotium:主要针对某一个APK进行自动化测试,APK可以有源码,也可以没有源码,功能强大;缺点是针对APK操作,而且需要对APK重新签名(有工具),因此操作相对复杂;


   

3、UiAutomator:优点:可以对所有操作进行自动化,操作简单;缺点:Android版本需要高于4.0,无法根据控件ID操作,相对来说功能较为局限,但也够用了;


   

4、Monkey:准确来说,这不算是自动化测试,因为其只能产生随机的事件,无法按照既定的步骤操作;


    由上面介绍可以有这样的结论:测试某个APK,可以选择Rubotium;测试过程可能涉及多个APK,选择UiAutomator;一些简单的测试,选择Monkeyrunner;


    本文主要介绍UiAutomator的使用方法。


三、环境搭建



3.1、必备条件:

    1、JDK


    2、SDK(API高于15)


    3、Eclipse(安装ADT插件)


    4、ANT(用于编译生成jar)


3.2、简要步骤:

   

1、安装JDK并添加环境变量。


        安装后,一定要通过JAVA_HOME的方式添加环境变量,即先建立JAVA_HOME变量,然后在path中添加%JAVA_HOME%\bin;


   

2、添加SDK环境变量。


        一定要先建立ANDROID_HOME,然后把%ANDROID_HOME%\tools添加到path中;


   

3、安装Eclipse,并安装ADT插件。
    4、安装ANT工具,并添加环境变量。


        同样一定要先建立%ANT_HOME%变量,然后在path中添加%ANT_HOME%\bin


四、详细操作



4.1、建立工程

    用Eclipse新建Java Project,

注意,不是Android Project!


4.2、添加JUnit库

    

andriod自动化测试 ios android手机自动化测试_UiAutomator


4.3、添加Android库

    找到路径Android-sdk\platforms\android-17\下面的android.jar和uiautomator.jar添加进来:


    

andriod自动化测试 ios android手机自动化测试_UiAutomator_02


    所有库添加完应该是这个样子:


    

andriod自动化测试 ios android手机自动化测试_Android_03


4.4、在src中添加包,然后添加class文件

    文件内容为:

package com;
    import com.android.uiautomator.core.UiObject;
    import com.android.uiautomator.core.UiObjectNotFoundException;
    import com.android.uiautomator.core.UiScrollable;
    import com.android.uiautomator.core.UiSelector;
    import com.android.uiautomator.testrunner.UiAutomatorTestCase;

    public class Runner extends UiAutomatorTestCase {

        public void testDemo() throws UiObjectNotFoundException {
            getUiDevice().pressHome();
            // 进入设置菜单
            UiObject settingApp = new UiObject(new UiSelector().text("Settings"));
            settingApp.click();
            //休眠3秒
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            // 进入语言和输入法设置
            UiScrollable settingItems = new UiScrollable( new UiSelector().scrollable(true));

            UiObject languageAndInputItem = settingItems.getChildByText(
                    new UiSelector().text("Language & input"), "Language & input", true);
            languageAndInputItem.clickAndWaitForNewWindow();
            
        }
    }

   

上面工程路径在e:\workspace\AutoRunner,类全名为com.Runner,至于具体的作用我们现在不去关心。


4.5、找到SDK ID

    CMD进入\Android-sdk\tools\目录下,运行命令:


    android list


    查看API大于15的SDK的ID值,当前是6;


    

andriod自动化测试 ios android手机自动化测试_andriod自动化测试 ios_04


4.6、创建build文件

    仍然在\Android-sdk\tools\目录下,运行命令:


    android create uitest-project -n <name> -t <android-sdk-ID> -p <path>


    比如:


    android create uitest-project -n AutoRunner -t 6 -p e:\workspace\AutoRunner


    上面的name就是将来生成的jar包的名字,可以自己定义,android-sdk-ID就是上面看到的6;path是Eclipse新建的工程的路径;运行命令后,将会在工程的根目录下生成build.xml文件。如果没生成,检查上面的步骤。


4.7、编译生成jar

    CMD进入项目的工程目录,然后运行ant build,将使用ant编译生成jar,成功将会提示:


    

andriod自动化测试 ios android手机自动化测试_UiAutomator_05


    然后会在bin目录下生成jar文件。


4.8、push并运行jar

    adb push <jar文件路径> data/local/tmp


    adb shell uiautomator runtest <jar文件名> -c <工程中的类名,包含包名>


    比如:


    adb push e:\workspace\AutoRunner\bin\AutoRunner.jar data/local/tmp


    adb shell uiautomator runtest AutoRunner.jar -c com.Runner


    然后就能看到手机会按照Runner中的步骤自动执行。具体效果就是,进入设置菜单,然后再进入“语言和输入法”菜单


五、代码分析


    我们从几个最重要的对象来介绍。


5.1、UiDevice对象

    getUiDevice()的方法可以得到一个UiDevice的对象,通过这个对象可以完成一些针对设备的动作:


    click(int x, int y)


    ----在(x,y)表示的像素地方点击


    pressBack()


    pressDelete()


    pressEnter()


    pressHome()


    pressMenu()


    pressSearch()


    ----点击相应的按键


    wakeUp()


    ----当手机处于灭屏状态时,唤醒屏幕,并解锁。


    swipe(startX, startY, endX, endY, steps)


    ----在手机上滑动,从(startX,startY)到(endX,endY)。steps表示滑动的这个距离分为几步完成,数目越少,滑动幅度越大。


    setOrientationLeft()


    setOrientationRight()


    ----将手机向相应方向旋转。


    setOrientationNatural()


    ----将手机旋转状态回归正常。


5.2、UiSelector对象

    这个对象可以理解为一种条件对象,描述的是一种条件,经常配合UiObject使用,可以得到某个(某些)符合条件的控件对象。


    checked(boolean val)


    ----描述一种check状态为val的关系。


    className(className)


    ----描述一种类名为className的对象关系


    clickable(boolean val)


    ----与checked类似,描述clickable状态为val的关系


    description(desc)


    ----不解释


    descriptionContains(desc)


    ----与description类似


    focusable(boolean val)


    ----与checked类似


    index(index)


    ----用当前对象在父对象集中的索引作为描述


    packageName(String name)


    ----用包名作为条件描述


    selected(val)


    ----描述一种选择关系


    text(text)


    ----最为常用的一种关系,用控件上的文本即可找到当前控件,

需要注意,所有使用text属性找到的控件,必须是英文的。也就是说,不支持通过中文查找控件!


    textContains(text)


    ----与text类似


    textStartsWith(text)


    ----与text类似


5.3、UiObject对象

    这个对象可以理解为控件的对象。 一般一个UiObject对象可以通过一下形式得到:


    UiObject mItem = new UiObject(new UiSelector().text("English"));


    也就是配合一个UiSelector就可以得到一个控件。


    click()


    ----点击控件


    clickAndWaitForNewWindow()


    ----点击某个控件,并等待窗口刷新


    longClick()


    ----长按


    clearTextField()


    ----清除文本,主要针对编辑框


    getChildCount()


    ----这个方法可以看出,其实UiObject也可以是一个控件的集合


    getPackageName()


    ----得到控件的包名


    getSelector()


    ----得到当前控件的选择条件


    getText()


    ----得到控件上的Text


    isCheckable()


    isChecked()


    isClickable()


    isLongClickable()


    isScrollable()


    isScrollable()


    isSelected()


    ----判断是否具备某个属性


5.4、UiCollection对象

    这个对象可以理解为一个对象的集合。因为UiSelector描述后得到的有可能是多个满足条件的控件集合,因此可以用来生成UiCollection:


    UiCollection mUiCollection = new UiCollection(new UiSelector().text("Settings"));


    getChild(selector)


    ----从集合中再次通过UiSelector选择一个UiObject对象


    getChildByDescription(childPattern, text)


    ----从一个匹配模式中再次以text为条件选择UiObject


    getChildByText(childPattern, text)


    ----与上面类似。


    getChildCount()


    ----得到当前集合中控件的个数


5.5、UiScrollable对象

    UiScrollable可以生成一个滚动动作的对象,其最大的作用就是可以实现滚动的查找某个元素。比如在“设置”菜单中,“语言和输入法”这个菜单比较靠下,需要滚动后才可以看到(找到),因此就用上了UiScrollable:


UiScrollable settingItems = new UiScrollable( new UiSelector().scrollable(true));
    UiObject languageAndInputItem = settingItems.getChildByText(
				new UiSelector().text("Language & input"), "Language & input",
				true);

    上面的形式就可以在滚动中查找显示有“Language & input”的控件,也就是“语言和输入法”的设置项。


5.6、等待操作和添加Log的方法

    如果是对于一个标准的UiObject对象,可以通过clickAndWaitForNewWindow的方法在点击之后主动等待一段事件,但是如果需要额外的等待一段时间,特别对于getUiDevice().pressHome();这种操作,可能需要很长的事件去为下一步操作获取更多的事件,此时我们可以使用线程的sleep方法去实现:

//等待3秒
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }

    而添加Log的方法也可以通过Java标准的println来实现:


    System.out.println("This used to print some log!!!" + setLanItem.getText());


    以上Log将会在jar被运行时通过CMD窗口打印出来。



六、一个相对完整的测试case


    下面就用一个相对连贯的测试用例来串一下上面的知识点,这个case用例要做的就是进入系统设置菜单,然后选择“语言和输入法”菜单,然后进入“语言设置”菜单,然后在第一项上点击,把当前语言设置为“简体中文”:


public void setChineseLan() throws UiObjectNotFoundException {
    //进入操作前,先用Home键进入待机界面
    getUiDevice().pressHome();


    //进入“系统设置”菜单。也可以通过点击menu按键来实现
    UiObject settingApp = new UiObject(new UiSelector().text("Settings"));
    settingApp.click();


    //等待3秒
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }


    //用滚动的方式查找并进入“语言和输入法设置”菜单
    UiScrollable settingItems = new UiScrollable(
            new UiSelector().scrollable(true));


    UiObject languageAndInputItem = settingItems.getChildByText(
            new UiSelector().text("Language & input"), "Language & input",
            true);
    languageAndInputItem.clickAndWaitForNewWindow();


    //找到“English”的可点击项(因为当前是英文环境)
    UiObject setLanItem = new UiObject(new UiSelector().text("English"));
    setLanItem.clickAndWaitForNewWindow();


    //Log输出
    System.out.println("setLanItem-->" + setLanItem.getPackageName());


    //由于无法识别中文,因此我们这里使用坐标去选择“简体中文”项
    getUiDevice().click(350, 250);
    
    //点击返回键,回到待机界面
    getUiDevice().pressBack();
    getUiDevice().pressBack();
    getUiDevice().pressBack();
}