美文网首页
Android Unit Test学习

Android Unit Test学习

作者: doubimonkey | 来源:发表于2016-09-21 12:14 被阅读0次

    Android Unit Test学习

    @(单元测试)[Android|Markdown]

    • 单元测试 这几天接触了下android的单元测试,写了一些小DEMO,自己总结下来,觉得android的单元测试可以有以下优缺点:
    • 优点
    1. 减少bug率
    2. 明确方法的输入输出
    3. 自测程序,使自己对代码更有自信
    • 缺点
    1. 耗时
    2. 有些时候测试环境于真实环境有差异,导致做了很多适配的工作。
    3. 不适合一个硬性指标。如代码覆盖率等。

    [TOC]

    Android Test 框架

    流行的android测试框架很多,可以参照这个网页:Android 测试工具。我主要调研过以下几种:

    1. 原生框架 :ActivityInstrumentationTestCase2
    2. Android Test Lib (谷歌官方提供)Espresso等
    3. Appium (可以编写测试脚本)
    4. Monkey 和Monkey Runner(随机测试,谷歌官方提供)
    5. Robolectric (第三方测试框架,不需要真机或者模拟器)
    6. Robotium (封装官方框架)

    上述几种也可以组合使用,基本上想对于一个项目做完整的单元测试,即包括UI,业务,数据,协议等,一般都要自己再<font color=red>封装一些适合项目的框架和工具类</font>。

    原生框架

    先上图~


    • AndroidTestCase类:
      提供系统对象(如Context)的方法。使用Context,你可以浏览资源,文件,数据库等等。不能测试涉及UI的方法。
    • InstrumentationTestCase类:
      继承TestCase类,并可以使用Instrumentation框架,用于测试Activity。使用Instrumentation,Android可以向程序发送事件进行自动UI测试,并可以精确控制Activity的启动,监测Activity生命周期的状态
      ActivityUnitTestCase设计用于单元测试,它在一个孤立的系统环境中测试Activity。换句话说,当你使用这个测试类时,Activity不能与其它Activity交互。每个测试用例都需要使用startActivity启动测试activity。

    单元测试在Studio中的应用

    Android Studio对单元测试完全支持,已经帮开发者搭建好了测试环境。测试开发者只需要关心测试用例编写,不用关心测试环境搭建,给测试开发带来极大便利。在Android Studio上进行JUnit单元测试步骤:
    1 编写测试用例。
    编写测试用例需要继承TestCase的子类。如果测试对象为普通java类,自定义测试用例需要继承AndroidTestCase;如果测试对象为Activity,可以继承ActivityUnitTestCase,也可以继承ActivityInstrumentationTestCase2。
    继承ActivityUnitTestCase和ActivityInstrumentationTestCase2不同之处在于,前者需要自己使用startActivity启动被测试的activity,并且该activity在一个独立的环境中运行。继承自ActivityInstrumentationTestCase2的测试用例不需要开发者自己调用被测试的activity,测试框架会自动启动activity。
    自定义测试用例类需要有构造函数,并且将测试目标类传递给父类。如:
    public LoadActivityTest() {

    super(LoadActivity.class);//LoadActivity为测试目标类
    

    }

    一般来说自定义测试用例需要重写setUp和tearDown,如果测试用例中有多个测试方法,setUp和tearDown会在每个测试方法前后被调用。setUp用来初始化测试环境,tearDown在每个测试方法结束后还原测试环境。
    使用断言assertXXX来判断测试结果。如果断言失败,则测试用例测试不通过,所有断言都成功,则测试用例测试通过。 常见的断言有:
    assertNotNull:断言测试对象不为NULL;
    assertNull:断言测试对象为NULL;
    assertEquals:断言测试的两个对象值相等;
    assertTrue:断言Boolean值为true;
    assertFalse:断言Boolean值为false。
    所有测试方法必须以test开头,如testXXX。测试框架会自动调用所有以test为开头的测试方法。各个测试方法没有运行先后顺序。因此,各个测试方法不应该有前后依赖顺序。

    UI测试。

    1)UI测试应该在主线程中操作。
    activity.runOnUiThread(new Runnable() {

    @Override
    
    public void run() {
    
        mPaymentButton.callOnClick();
    
    }
    

    });

    2)模拟系统发送按键事件
    this.sendKeys(KeyEvent.KEYCODE_HOME);

    3)点击按键事件
    mPaymentButton.callOnClick();

    3 线程同步问题
    1)测试线程和异步任务线程同步
    在单元测试中,有很多异步任务。我们需要异步任务返回结果再测试,这就需要用到线程同步相关知识。比如,我们测试的单元需要发送一个网络请求,需要测试返回结果是否正确。这是一个典型的异步任务,这就需要用本节的方法去做单元测试。
    CountDownLatch,一个同步辅助类,用信号量机制实现线程同步。主要方法有:
    public CountDownLatch(int count);
    构造函数,count指定了计数器的次数。可以理解为加的锁数量。
    public void await() throws InterruptedException
    调用此方法会一直阻塞当前线程,直到计数器的值为0。
    public void countDown();
    每次调用计数减少1,当计数为0时,线程被唤醒。
    2)测试线程和UI主线程同步
    每个测试方法都运行在子线程中,在某些情况,测试子线程需要等待主线程(UI线程)空闲才继续运行测试子线程。这就需要同步测试子线程和UI主线程。Instrumentation为我们提供了waitForIdleSync方法来满足这类同步需求。使用方法如下:

    public void testExample(){

    ......
    
    getInstrumentation().waitForIdleSync();//等待主线程返回
     .....
    

    }

    需要注意:waitForIdleSync只能在测试子线程中被调用。测试方法默认运行在子线程中,但也可以运行在UI线程中,只需要在测试方法前加上标注@UiThreadTest,这样整个测试方法都在UI线程中运行。

    测试Fragment

    由于JUnit没有提供对fragment测试的框架,所以只能在使用activity测试框架。通常的做法是使用ActivityInstrumentationTestCase2测试框架,先加载一个activity,然后在setup中启动被测试的fragment。这样在每个测试方法执行前都会启动被测试的fragment。
    使用ActivityInstrumentationTestCase2框架对fragment进行单元测试的不足之处在于,测试任何一个单元(函数),都需要先启动该fragment,然后才能进行单元测试。

    五 单元测试基础要点
    1 方法名称必须以test开头。
    2不能依赖测试方法顺序,每个测试方法都是在子线程中运行。
    3 setUp方法和tearDown方法都是TestCase类的方法
    1)setUp方法是在执行每个测试方法之前执行的
    2)tearDown方法是在执行每个测试方法之后执行的
    4 涉及UI操作的应该在主线程中执行。
    5 waitForIdleSync和sendKeys不允许在UI线程里运行,只能运行在测试子线程中。

    原理

    需要说明的是,在Android系统中,测试程序也是应用程序,我们可以将其看成一个没有UI的应用。
    其实现过程大致如下:如图,InstrumentationTestRunner通过调用Instrumentation杀除应用程序的进程,再用Instrumentation重启该应用。这时,测试应用和被测应用就运行在同一进程下。测试应用怎么知道该测试哪个应用呢?嗯,这是通过在测试工程的mainfest文件中添加元素来实现的。当测试应用和被测应用运行在同一个进程里,它们之间就可以通过Instrumentation来进行消息交互,从而达到测试效果。当Instrumentation与某个程序交互时,其大致采用如下步骤:(资料来源:
    http://blog.csdn.net/fireworkburn/article/details/20144153)。
    首先,启动时,初始化测试APK的配置文件AndroidManifest.xml文件中。该配置文件中标明了所使用的测试运行类、被测目标应用、包名等。然后,启动被测应用的Activity。同时,将测试ActivityThread做为一个引用进行初始化。此时,如果找不到目标应用则会报错。其次,执行测试脚本。测试时,测试工程中任何对目标应用进行的操作,都会用异步的方式,将消息体放在目标程序的MessageQueue中。这样,目标程序在查看到自己的MessageQueue中有内容时就会执行。

    Robolectric

    根据我的研究以及我个人的需求,我觉得<font color=red>Robolectric</font>更适合开发使用,因为其速度快,而且不需要在真机上看效果,这样可以把代码逻辑都写好,验证完毕在真机上一次运行。大大提高代码质量和效率。个人见解,不喜勿喷。

    Robolectric简介

    robolectric官网介绍,Robolectric是为android上的TDD开发而产生的一款第三方测试框架,按照官网说的:

    Robolectric is a unit test framework that de-fangs the Android SDK jar so you can test-drive the development of your Android app. Tests run inside the JVM on your workstation in seconds. With Robolectric you can write tests like this:

    @RunWith(RobolectricTestRunner.class)
    public class MyActivityTest {
    
      @Test
      public void clickingButton_shouldChangeResultsViewText() throws Exception {
        MyActivity activity = Robolectric.setupActivity(MyActivity.class);
    
        Button button = (Button) activity.findViewById(R.id.button);
        TextView results = (TextView) activity.findViewById(R.id.results);
    
        button.performClick();
        assertThat(results.getText().toString()).isEqualTo("Robolectric Rocks!");
      }
    }
    

    该框架测试起来是很方便的,但是同时由于不启动app,可能遇到一些问题,总体上使用起来还是方便的。

    eclipse配置

    新建 Test Project

    这里我新建了一个 Maven Project

    File> New > Other > Maven Project ,跟 Appium 里面使用 Java 写脚本,建立 Maven 项目是相同的。

    我建立的测试项目 roboletrictest,pom.xml 的内容我这里贴下:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.example</groupId>
      <artifactId>roboletrictest</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    
      <name>roboletrictest</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
        </dependency>
    
        <dependency>
          <groupId>org.robolectric</groupId>
          <artifactId>robolectric</artifactId>
          <version>3.0</version>
          <scope>test</scope>
        </dependency>
    
      </dependencies>
    </project>
    

    配置 Test Project

    配置 roboletrictest 的 Build Path

    右键项目(roboletrictest) > Build Path > Configure Build Path
    在 Project 选项中 add 被测项目(roboletric)
    在 Libraries 中 Add External JARs,添加对应的 android.jar,android.jar 在 sdk 中(需要下载)
    完成 Test Case

    在 src/test/java 目录下 new 一个 package,对应被测项目(com.example.roboletric.test)
    在包里面 new 一个 JUnit Test Case,使用 Junit 4,Class Name 为 MainActivityTest
    Run Test Case

    右键 MainActivityTest > Run As > JUnit Test

    自己打包依赖

    因为是maven项目,其实你也可以自己把roboletric的项目打包,使用maven-assembly-plugin把项目打成jar包,直接导入使用。以前的官网提供了依赖包的,但是现在找不着了。

    Android studio配置

     repositories {
            maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
            mavenLocal()
            mavenCentral()
        }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.0.0'
        testCompile "junit:junit:4.10"
        testCompile "org.assertj:assertj-core:1.7.0"
        testCompile "org.robolectric:robolectric:${robolectricVersion}"
    }
    

    Shadow类的使用

    Android Test Kit

    Android Test Kit 是一组 Google 开源测试工具,用于 Android 平台,包含 Espresso API 可用于编写简洁可靠的 Android UI 测试。它也可以跟其他的框架混合使用。
    github Demo的地址:
    https://github.com/googlesamples/android-testing

    Monkey 和MonkeyRunner

    Appium

    框架基本原理

    下次再看,我会开一篇代理设计模式的专栏

    常见的测试方法

    测试UI

    一般的测试框架都提供了对UI的测试,具体可以参考谷歌源码对一些原生app做的测试。

    测试业务

    建议把业务方法的入口都放在manager里,这样测试的时候直接一调用就OK了。但是记住要测试一些边界情况,例如空指针之类的。

    测试http请求

    因为http请求是异步的,这块可能要对请求的框架做一些封装,以便支持mock数据。

    相关文章

      网友评论

          本文标题:Android Unit Test学习

          本文链接:https://www.haomeiwen.com/subject/nmlaettx.html