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,业务,数据,协议等,一般都要自己再封装一些适合项目的框架和工具类

原生框架

先上图~


Android Unit Test学习_第1张图片
  • 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() {

@Overridepublic 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

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

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 的内容我这里贴下:

  4.0.0  com.example  roboletrictest  0.0.1-SNAPSHOT  jar  roboletrictest  http://maven.apache.org      UTF-8              junit      junit      4.12      test              org.robolectric      robolectric      3.0      test      

配置 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数据。

更多相关文章

  1. Android实现在一个activity中添加多个listview的方法
  2. [置顶] Android 65K问题之Multidex原理分析及NoClassDefFoundErr
  3. 【自动化测试】Android Monkey实践
  4. 子线程新建Handler为什么会报错?——浅谈Handler、Looper、Messag
  5. Android 通用获取Ip的方法(判断手机是否联网的方法)!!!
  6. Android初识多线程
  7. Android获取音视频原始流数据方法详解

随机推荐

  1. Android多线程--Handler
  2. android camera HAL v3.0概述
  3. Android中AVD(Android Virtual Device)不能
  4. android 的build.gradle 的API手册
  5. android广播接收者 ip电话拔号器
  6. Android软键盘的隐藏显示研究
  7. Handler+Message+MessageQuque+Looper 异
  8. Android(安卓)百度定位SDK(v3.7.0)结合方
  9. 在Android模拟器中安装和卸载apk应用
  10. Android通过Alpha实现渐变效果的几个方法