那么单元测试如何开始呢

一.创建测试类

在任意需要测试的类(或者方法)下面按下Ctrl+Shift+T(这是默认热键)如下图**

android 如何添加单元测试 安卓单元测试的步骤_Test

首次创建一个新的测试类,然后会弹出提示界面:

android 如何添加单元测试 安卓单元测试的步骤_android 如何添加单元测试_02

如果你已经创建过,则会提示对应的测试类让你跳转过去,同样测试类也可以利用这个方法跳转到被测试类。

android 如何添加单元测试 安卓单元测试的步骤_android 如何添加单元测试_03

创建一个测试类,并编写测试代码,如下图

android 如何添加单元测试 安卓单元测试的步骤_Android JUnit_04

二.分析运行结果

运行成功时

android 如何添加单元测试 安卓单元测试的步骤_测试类_05

运行失败时

android 如何添加单元测试 安卓单元测试的步骤_android 如何添加单元测试_06

三.批量测试和生产报告

假设你只有一个类或者有几个类需要测试的话,那可以直接使用上文说的方法来测试,但是假设你有很多的类和方法需要测试的话上面的操作就显得是十分笨拙。Android Studio的Gradle插件为我们生成了三个任务:

  • testDebugUnitTest
  • testReleaseUnitTest
  • test

其中前两个任务是分别执行为Debug和Release模式下的所有单元测试,第三个任务就是执行前面两个任务。

  • 方法:可以直接在面板中选择Task执行(使用本地的Gradle)

android 如何添加单元测试 安卓单元测试的步骤_Android JUnit_07

等待执行完成就可以看到build/reports/tests/目录下对应的Html报告:

android 如何添加单元测试 安卓单元测试的步骤_android 如何添加单元测试_08

使用浏览器打开可以看到详细测试报告:

android 如何添加单元测试 安卓单元测试的步骤_android 如何添加单元测试_09

四.Assert类中主要方法

方法名

含义

assertEquals

断言传入的预期值与实际值是相等的

assertNotEquals

断言传入的预期值与实际值是不相等的

assertArrayEquals

断言传入的预期数组与实际数组是相等的

assertNull

断言传入的对象是为空

assertNotNull

断言传入的对象是不为空

assertTrue

断言条件为真

assertFalse

断言条件为假

assertSame

断言两个对象引用同一个对象,相当于“==”

assertNotSame

断言两个对象引用不同的对象,相当于“!=”

assertThat

断言实际值是否满足指定的条件

注意:上面的每一个方法,都有对应的重载方法,可以在前面加一个String类型的参数,表示如果断言失败时的提示。

assertThat用法

上面我们所用到的一些基本的断言,如果我们没有设置失败时的输出信息,那么在断言失败时只会抛出AssertionError,无法知道到底是哪一部分出错。而assertThat就帮我们解决了这一点。它的可读性更好。

assertThat(T actual, Matcher<? super T> matcher);

assertThat(String reason, T actual, Matcher<? super T> matcher);

其中reason为断言失败时的输出信息,actual为断言的值,matcher为断言的匹配器。

下图为使用assertThat测试失败时所显示的具体错误信息。可以看到错误信息很详细!

android 如何添加单元测试 安卓单元测试的步骤_Android 单元测试_10

常用的匹配器整理

匹配器

说明

例子

is

断言参数等于后面给出的匹配表达式

assertThat(5, is (5));

not

断言参数不等于后面给出的匹配表达式

assertThat(5, not(6));

equalTo

断言参数相等

assertThat(30, equalTo(30));

equalToIgnoringCase

断言字符串相等忽略大小写

assertThat(“Ab”, equalToIgnoringCase(“ab”));

containsString

断言字符串包含某字符串

assertThat(“abc”, containsString(“bc”));

startsWith

断言字符串以某字符串开始

assertThat(“abc”, startsWith(“a”));

endsWith

断言字符串以某字符串结束

assertThat(“abc”, endsWith(“c”));

nullValue

断言参数的值为null

assertThat(null, nullValue());

notNullValue

断言参数的值不为null

assertThat(“abc”, notNullValue());

greaterThan

断言参数大于

assertThat(4, greaterThan(3));

lessThan

断言参数小于

assertThat(4, lessThan(6));

greaterThanOrEqualTo

断言参数大于等于

assertThat(4, greaterThanOrEqualTo(3));

lessThanOrEqualTo

断言参数小于等于

assertThat(4, lessThanOrEqualTo(6));

closeTo

断言浮点型数在某一范围内

assertThat(4.0, closeTo(2.6, 4.3));

allOf

断言符合所有条件,相当于&&

assertThat(4,allOf(greaterThan(3), lessThan(6)));

anyOf

断言符合某一条件,相当于或

assertThat(4,anyOf(greaterThan(9), lessThan(6)));

hasKey

断言Map集合含有此键

assertThat(map, hasKey(“key”));

hasValue

断言Map集合含有此值

assertThat(map, hasValue(value));

hasItem

断言迭代对象含有此元素

assertThat(list, hasItem(element));

自定义匹配器

只需要继承BaseMatcher抽象类,实现matches与describeTo方法。代码如下:

public class IsMobilePhoneMatcher extends BaseMatcher<String> {
    /**
     * 进行断言判定,返回true则断言成功,否则断言失败
     */

    @Override
    public boolean matches(Object item) {
        if (item == null) {
            return false;
        }

        Pattern pattern = Pattern.compile("(1|861)(3|5|7|8)\\d{9}$*");
        Matcher matcher = pattern.matcher((String) item);

        return matcher.find();
    }

    /**
     * 给预期(Expected)断言成功的对象增加描述
     */
    @Override
    public void describeTo(Description description) {
        description.appendText("预计此字符串是手机号码!");
    }

    /**
     * 给断言失败的对象增加描述
     */
    @Override
    public void describeMismatch(Object item, Description description) {
        description.appendText(item.toString() + "不是手机号码!");
    }
}
@Test
 public void testPhone(){
   Assert.assertThat("19900003333",new IsMobilePhoneMatcher());
 }

执行结果如下图

android 如何添加单元测试 安卓单元测试的步骤_Android JUnit_11

五.JUnit Annotation(注解)

还记得上边创建测试类的时候出现了setUp和tearDown两个方法吗?分别对应@Before和@After这两个注解。实际上根据JUnit框架的设计,每个单元测试方法可以简单划分为:

  • setUp 对应 @Before注解的方法
  • test 对应 @Test注解的方法
  • tearDown 对应 @After注解的方法

如果创建时勾选这两个方法,

android 如何添加单元测试 安卓单元测试的步骤_Test_12


则会生成:

@Before
public void setUp() throws Exception {
}

@After
public void tearDown() throws Exception {
}

注意看看@Test注解的注释,可以看到,它可以接受两个参数,

  • 一个是预期异常
  • 一个是超时时间
//预期异常,不报错(如果不出现异常则报错)
@Test(expected=IndexOutOfBoundsException.class)
public void outOfBounds() {
    new ArrayList().get(1);
}

//超时报错
@Test(timeout=100) 
public void infinity() {
   while(true);
}

//这种情况要小心,注意误差问题,有可能正确,有可能错误
@Test(timeout=100) 
public void sleep100() {
   Thread.sleep(100);
}

注解名

含义

@Test

表示此方法为测试方法

@Before

在每个测试方法前执行,可做初始化操作

@After

在每个测试方法后执行,可做释放资源操作

@Ignore

忽略的测试方法

@BeforeClass

在类中所有方法前运行。此注解修饰的方法必须是static void

@AfterClass

在类中最后运行。此注解修饰的方法必须是static void

@RunWith

指定该测试类使用某个运行器

@FixMethodOrder

指定测试类中方法的执行顺序

@Rule

重新制定测试类中方法的行为

执行顺序:@BeforeClass –> @Before –> @Test –> @After –> @AfterClass

@Rule用法

还记得一开始我们在@Before@After注解的方法中加入”开始测试”、“结束测试”的提示信息吗?假如我们一直需要这样的提示,那是不是需要每次在测试类中去实现它。这样就会比较麻烦。这时你就可以使用@Rule来解决这个问题,它甚至比@Before@After还要强大。

自定义@Rule很简单,就是实现TestRule接口,实现apply方法。代码如下:

public class MyRule implements TestRule {
    @Override
    public Statement apply(final Statement base, final Description description) {

        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                String classNmae=description.getClassName();//获取测试类名
                String methodName=description.getMethodName();//获取测试方法名

                System.out.println(classNmae+"类下的"+methodName+"方法,开始测试");

                base.evaluate(); //运行的测试方法

                System.out.println(classNmae+"类下的"+methodName+"方法,结束测试");
            }
        };
    }
}

android 如何添加单元测试 安卓单元测试的步骤_Test_13


运行结果

android 如何添加单元测试 安卓单元测试的步骤_Test_14