UIAutomator2.0详解(JUnit Annotation篇)

在入门篇,我们提到了UIAutomator2.0与1.0的不同。其中,2.0基于JUnit,使用Annotation声明测试案例,是一个重要的特点。本篇将通过实例来讲述几个常用Annotation的使用。

不论何种测试,都需要初始化平台环境,遍历测试案例,为每个测试案例初始化测试上下文,并按照一定的顺序执行测试。
Annotation也是一样的思路。

(1)@BeforeClass:用于声明方法,该方法将在测试案例集(class)实例创建前执行。因此,该方法必须为static类型,仅被执行一次。相当于初始化测试平台环境。

(2)@AfterClass:用于声明方法,该方法将在测试案例集合(class)实例销毁前执行。因此,该方法也必须为static类型,仅被执行一次,相当于清理测试平台环境。

(3)@Before:用于声明方法,该方法将在每个测试案例(function)被调用前执行,因此,该方法可根据测试案例数量,被多次执行,相当于初始化测试上下文。

(4)@After:用于声明方法,该方法将在每个测试案例(function)被调用后执行,因此,该方法可根据测试案例数量,被多次执行,相当于清理测试上下文。

(5)@Test:用于声明方法,该方法将被视为测试案例(function)被执行。

(6)@Ignore:用于声明方法,表示忽略该测试案例。

我们来看一个完整简单案例

RunWith(AndroidJUnit4.class)
public class SalaryShowAppTest {

    private static final String TAG="APPUITest";
    private String mPackageName="com.breakloop.salaryshow";
    private String mLaunchActivityName=".MainActivity";

    public  static UiDevice mDevice;

    @BeforeClass
    public static void init(){
        Log.i(TAG, "init ");
        mDevice=UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    }

    @Before
    public void OpenAPP(){
        Log.i(TAG, "OpenAPP ");
        try {
            if(!mDevice.isScreenOn()){  //唤醒屏幕
                mDevice.wakeUp();
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        Utils.startAPP(mPackageName);  //启动app
        mDevice.waitForWindowUpdate(mPackageName, 2 * 2000);
    }

    @Test
    public void case1(){
        Log.i(TAG, "case1 ");
    }

    @Test
    public void case3(){
        Log.i(TAG, "case3 ");
    }

    @Test
    public void case20(){
        Log.i(TAG, "case20 ");
    }

    @Test
    public void case21(){
        Log.i(TAG, "case21 ");
    }

    @Test@Ignore
    public void case5(){
        Log.i(TAG, "case5 ");
    }

    @After
    public void closeAPP(){
        Log.i(TAG, "closeAPP ");
        Utils.closeAPP(mDevice,mPackageName);
    }

    @AfterClass
    public static void destroy(){
        Log.i(TAG, "destroy ");
        mDevice=null;
    }
}

Utils文件内容为

public class Utils {
    public static void  startAPP(String sPackageName){
        Context mContext = InstrumentationRegistry.getContext();

        Intent myIntent = mContext.getPackageManager().getLaunchIntentForPackage(sPackageName);  //启动app
        mContext.startActivity(myIntent);
    }

    public static void closeAPP(UiDevice uiDevice, String sPackageName){
        try {
            uiDevice.executeShellCommand("am force-stop "+sPackageName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void startAPP(UiDevice uiDevice,String sPackageName, String sLaunchActivity){
        try {
            uiDevice.executeShellCommand("am start -n "+sPackageName+"/"+sLaunchActivity);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我们运行测试案例集合,并查看日志

I/APPUITest: init 
I/APPUITest: OpenAPP 
I/APPUITest: case20 
I/APPUITest: closeAPP 
I/APPUITest: OpenAPP 
I/APPUITest: case21 
I/APPUITest: closeAPP 
I/APPUITest: OpenAPP 
I/APPUITest: case1 
I/APPUITest: closeAPP 
I/APPUITest: OpenAPP 
I/APPUITest: case3 
I/APPUITest: closeAPP 
I/APPUITest: destroy

看完LOG,那么问题来了~
Test Case的执行顺序是如何决定的?
默认情况下,是按照Test Case名称的HASH值进行排序。若HASH值相同,则以字母字典顺序执行。

当然Test Case的执行顺序,也可使用Annotation来指定。
(7)@FixMethodOrder(order):用于修饰测试案例集合类(class)。order有三种取值:MethodSorters.DEFAULT(默认方式),MethodSorters.NAME_ASCENDING(字母字典方式),MethodSorters.JVM(由JVM自己决定)。

这里我们看一下字母字典方式执行顺序。

I/APPUITest: init 
I/APPUITest: OpenAPP 
I/APPUITest: case1
I/APPUITest: closeAPP 
I/APPUITest: OpenAPP 
I/APPUITest: case20 
I/APPUITest: closeAPP 
I/APPUITest: OpenAPP 
I/APPUITest: case21 
I/APPUITest: closeAPP 
I/APPUITest: OpenAPP 
I/APPUITest: case3 
I/APPUITest: closeAPP 
I/APPUITest: destroy

JVM顺序方式,存在一定的随机性,通常不做考虑。
在本地做了几次JVM顺序的执行,发现每次的执行顺序都与NAME_ASCENDING一致。可能不同的JVM不同,或者存在其他影响因素,这里不再深究。有知晓的同学,还望指点。

我们来做一下扩展~

(8)@Test(timeout=XXX):用于声明方法,该方法必须在XXX毫秒内完成,否则视为失败测试案例。

我们对Test Case1进行修改

    @Test(timeout = 1)
    public void case1(){
        Log.i(TAG, "case1: ");
        for (int index=1;index<100;index++){
            Log.i(TAG, "index="+index);
        }
    }

运行,并查看LOG和结果

I/APPUITest: init 
I/APPUITest: OpenAPP 
I/APPUITest: case1
I/APPUITest: index=1
...
I/APPUITest: index=99
I/APPUITest: closeAPP
I/APPUITest: destroy: 
org.junit.runners.model.TestTimedOutException: test timed out after 1 milliseconds
at android.util.Log.println_native(Native Method)
at android.util.Log.i(Log.java:180)
...
Tests ran to completion.

可见,IDE是将测试案例完全执行完毕后,再与Timeout进行比对,判断测试案例是否成功。
当然,我们可以通过,修改timeout(Long型),或减少任务量等方式,将Test Case成功执行。这里不再啰嗦。

(9)@Test(expected=XXException.class):用于声明方法,该方法应当抛出XXException异常,若未抛异常或抛出其他异常,则视为失败测试案例。

我们队Test Case3进行修改

    @Test(expected = IOException.class)
    public void case3(){
        Log.i(TAG, "case3: ");
    }

运行,并查看LOG和结果

I/APPUITest: init 
I/APPUITest: OpenAPP 
I/APPUITest: case3
I/APPUITest: closeAPP
I/APPUITest: destroy
java.lang.AssertionError: Expected exception: java.io.IOException
...
Tests ran to completion.

我们可以手动抛出异常,使得测试案例成功执行。

    @Test(expected = IOException.class)
    public void case3() throws IOException {
        Log.i(TAG, "case3: ");
        throw new IOException();
    }

至此,常用的Annotation介绍完毕。若有遗漏,之后再做补充。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值