Android 自动化测试二

前言

在阅读本文之前,强烈建议仔细阅读上文了解Junit3的一些相关内容,或者提前熟悉Junit3的一些简单使用规则,这样学junit4会更容易

1.junit3 和 junit4之间区别

先回顾一下junit3 的特点:

1.需要继承TestCase
2.需要以test开头才可以框架识别
3.需要写较多的代码完成一个简单的测试
4.初始化和回收代码在每个测试方法之前调用,一些方法其实可以统一初始化的,而
5.无需每次测试前都初始化一遍,也即不支持类初始化

Junit4是在junit3的基础上进行扩展,增加一些java 1.5的新特性,而最大的就是支持注解方式,简化冗余代码量,更容易写测试代码。

1.1 写法

首先需要引入junit4的测试环境,(这些新版IDE工具已经帮你完成了)

本文使用 android studio 2.2.3 ,gradle com.android.tools.build:gradle:2.2.3,新建工程自动引入单元测试依赖 junit:junit:4.12,需要注意的是系统sdk中集成的是 junit3,需要用新的测试框架,需要自动或者手动依赖 junit 4的jar

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.1"
    defaultConfig {
        applicationId "nuoyuan.com.myapplication"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    testCompile 'junit:junit:4.12'

在这里进行一个对比(使用自动生成的方式,如何自动生成,上篇bolg有介绍)

Junit3

package nuoyuan.com.myapplication.junit3;

import junit.framework.TestCase;

/**
 * Created by weichyang on 2017/1/16.
 * junit 3单元测试
 */
public class MathUtilsTest extends TestCase {

    public void testAdd() throws Exception {

    }

    public void testJian() throws Exception {

    }

    public void testCheng() throws Exception {

    }

    public void testChu() throws Exception {

    }

Junit4

package nuoyuan.com.myapplication.junit4;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * Created by weichyang on 2017/1/16.
 */
public class MathUtilsTest {

    @Before
    public void setUp() throws Exception {

    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void add() throws Exception {

    }

    @Test
    public void jian() throws Exception {

    }

    @Test
    public void cheng() throws Exception {

    }

    @Test
    public void chu() throws Exception {

    }

}

具体的写法就可以在具体的测试方法的中调用。这里不进行赘述……..

1.2 易用性

1.首先从结构上面看,很明显junit4的结构更加清晰明了,命名也更加灵活。
2.使用注解,让测试代码写起来也更加得心应手,方便自不用说,用过的人都说好。
3.junit增加一些新的测试Api,既然是junit3的一个扩展,在兼容junit3现有功能上又增加 几个常用的测试Api。

2.junit4常用Api介绍

这里依然使用上面的生成代码进行介绍

@Before @After

Junit3中,我们重写TestCase的setUp和tearDown方法就可以实现每个测试方法测试前后的初始化和回收,Junit4中,仅需要在方法前面加上@Before、@After即可快速实现相同的功能:

ackage nuoyuan.com.myapplication.junit4;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import nuoyuan.com.myapplication.utils.MathUtils;

/**
 * Created by weichyang on 2017/1/16.
 */
public class MathUtilsTest {

    @Before
    public void setUp() throws Exception {
        System.out.print("start 执行\n");
    }

    @After
    public void tearDown() throws Exception {
        System.out.print("end 执行 \n");
    }

    @Test
    public void add() throws Exception {
        System.out.print("+++++++++++++++++++++++++++++++add 执行\n");
        Assert.assertEquals(2, MathUtils.add(1, 1));
    }

    @Test
    public void jian() throws Exception {
        System.out.print("--------------------------------jian 执行\n");
        Assert.assertEquals(2, MathUtils.jian(3, 1));
    }

    @Test
    public void cheng() throws Exception {

    }

    @Test
    public void chu() throws Exception {

    }

}

运行结果:

这里写图片描述

@BeforeClass @AfterClass

只需要在测试类中添加相应的方法,并在前面添加相应的注解就可以实现类参数初始化任务(添加@BeforeClass @AfterClass 注解的方法应该被static进行修饰)


import nuoyuan.com.myapplication.utils.MathUtils;

/**
 * Created by weichyang on 2017/1/16.
 */
public class MathUtilsTest {


    @BeforeClass
    public static void beforeClass() throws Exception {
        System.out.print("beforeClass 执行\n");
    }

    @AfterClass
    public  static void afterClass() throws Exception {
        System.out.print("afterClass 执行\n");
    }

    ...
...
...
}

结果

这里写图片描述

@Ignore


import nuoyuan.com.myapplication.utils.MathUtils;

/**
 * Created by weichyang on 2017/1/16.
 */
public class MathUtilsTest {


    @BeforeClass
    public static void beforeClass() throws Exception {
        System.out.print("beforeClass 执行\n");
    }

    @AfterClass
    public  static void afterClass() throws Exception {
        System.out.print("afterClass 执行\n");
    }

@Test
   public void ignore() {
    System.out.print("ignore 执行\n");
    Assert.assertEquals(3, MathUtils.add(1, 1));
  }

    ...
...
...
}

注意上面Ignore()方法是个异常

这里写图片描述

现在我们修改相应的注解@Ignore

mport nuoyuan.com.myapplication.utils.MathUtils;

/**
 * Created by weichyang on 2017/1/16.
 */
public class MathUtilsTest {


    @BeforeClass
    public static void beforeClass() throws Exception {
        System.out.print("beforeClass 执行\n");
    }

    @AfterClass
    public  static void afterClass() throws Exception {
        System.out.print("afterClass 执行\n");
    }

  @Ignore
   public void ignore() {
    System.out.print("ignore 执行\n");
    Assert.assertEquals(3, MathUtils.add(1, 1));
  }

    ...
...
...
}

运行

这里写图片描述

即使该方法测试不通过,添加@Ignore 注解,整体测试不会受到影响

异常测试

由于时间限制,不再重复造轮子。这里引用这边blog作者的总结,这里表示感谢

package com.czt.saisam.unittest.util.junit4;

import junit.framework.Assert;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.core.Is.is;

/**
 * 某些场合下,方法会抛出异常,但是并不是说抛出异常就表示方法出现问题,比如参数不合法异常,就表示参数传入有问题,方法是没有问题的,因此我们需要捕捉这种"正常"的异常
 * <p/>
 * 无论是expected还是expect都表示期望抛出的异常,
 * 假如某一方法,当参数为某一值时会抛出异常,
 * 第一种方法:必须为该参数单独写一个测试方法来测试异常,而无法与其他参数值一同写在一个测试方法里,所以显得累赘。
 * 第二种方法:习惯上是这样写,进行捕获然后抛出。
 * 第三种方法:不仅能动态更改期望抛出的异常,与断言语句结合的也非常好,因此推荐使用该方法来测试异常。
 *
 * @author zhitao
 * @since 2015-07-06 23:43
 */
public class ExceptionJunit4TestCase {

    /**
     * 第一种异常捕捉测试
     */
    @Test(expected = IndexOutOfBoundsException.class)
    public void empty() {
        new ArrayList<Object>().get(0);
    }

    /**
     * 第二种异常捕捉测试
     */
    @Test
    public void testExceptionMessage() {
        try {
            new ArrayList<Object>().get(1);
            Assert.fail("Expected an IndexOutOfBoundsException to be thrown");
        } catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
            org.junit.Assert.assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
        }
    }

    // 第三种异常捕捉测试
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
        List<Object> list = new ArrayList<Object>();

        thrown.expect(IndexOutOfBoundsException.class);
        thrown.expectMessage("Index: 0, Size: 0");
        list.get(0);
        Assert.assertEquals(1, list.get(0));
    }
}

运行,如果符合的自己预期的异常被捕获,测试通过,否则抛出异常测试不通过

运行

这里写图片描述

限时测试

顾名思义就是在规定的时间内完成测试,否则测试不通过,使用场景就是测试异步任务等一些的需要不同线程配合的任务

package com.czt.saisam.unittest.util.junit4;

import com.czt.saisam.unittest.util.AsyncTask;

import junit.framework.Assert;

import org.junit.Test;

/**
 * by weichayang
 */

public class AsyncTaskJunit4TestCase {

    @Test(timeout = 3000)
    public void sync_getOnlineConfig() {
        Assert.assertEquals("value1", new AsyncTask().sync_getOnlineConfig("key1"));
    }


}

测试的方法

package com.czt.saisam.unittest.util;

/**
 * by weichaoyang
 */
public class AsyncTask {

    public AsyncTask() {
        super();
    }

    public String sync_getOnlineConfig(String key) {
        // 采用线程睡眠模拟耗时操作
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if ("key1".equals(key)) {
            return "value1";
        }
        return null;
    }

    public void async_getOnlineConfig(String key, onFinishListener listener) {
        String value = sync_getOnlineConfig(key);
        if (listener != null) {
            listener.onFinish(key, value);
        }
    }

    public interface onFinishListener {

        void onFinish(String key, String value);

    }
}

结果:

这里写图片描述

异常,修改时间为500ms


public class AsyncTaskJunit4TestCase {

    @Test(timeout = 500)
    public void sync_getOnlineConfig() {
        Assert.assertEquals("value1", new AsyncTask().sync_getOnlineConfig("key1"));
    }


}

结果

这里写图片描述

@RunWith

在上文Junit3使用中,我们说到,Junit的运作模式: TestCase -> TestSuite -> TestRunner ==> TestResult,但是上文并没有怎么讲到TestRunner,这是因为在Junit4中讲解比较容易理解。

参数化测试 @Parameterized

在Junit3中的参数化测试,不断copy同一个测试方法进行判断类似:

Assert.assertEquals(2, MathUtils.add(1, 1));
Assert.assertEquals(2, MathUtils.add(2, 1));
Assert.assertEquals(2, MathUtils.add(3, 1));
Assert.assertEquals(2, MathUtils.add(4, 1));

而在JUnit4中可以更加优雅的进行测试,只需要使用参数化注解进行修饰就可以
同时,将所有的参数列为一个数组,并修饰为@Parameterized.Parameters(),然后,通过自定义构造函数,为每组测试参数赋值,然后调用测试方法进行测试:该类必须提供至少三个实体:

1.一种生成和返回测试数据的静态方法,
2.存储测试数据的单个构造函数,和
3.一个测试。

生成的测试数据的方法,必须以进行注释@Parameters,并且它必须返回阵列的集合。每个数组表示在特定测试运行中使用的数据。每个数组中的元素数量必须与类的构造函数中的参数数量相对应,因为每个数组元素都将被传递给构造函数,一次一个,因为类被反复实例化。

当调用测试运行器时,将执行数据生成方法,并且它将返回数组集合,其中每个数组是一组测试数据。然后测试运行器将实例化类并将第一组测试数据传递给构造函数。构造函数将在其字段中存储数据。然后将执行每个测试方法,并且每个测试方法将访问该第一组测试数据。在每个测试方法执行后,对象将被再次实例化,这次使用数组集合中的第二个元素,依此类推。


/**
 * by weichaoyang
 */
@RunWith(Parameterized.class)
public class StringUtilJunit4TestCase {

    @Parameterized.Parameters()
    public static Iterable<Object[]> data() {
        return Arrays.asList(new Object[][] {
                {true, 123}, {true, new String[] {null, null}}, {true, new String[] {""}}, {true, new String[] {"", ""}},
                {false, new String[] {"123"}}, {true, new String[] {"123", ""}}, {false, new String[] {"23", "6"}}
        });
    }

    private boolean mExpected=true;

    private String[] mArgs;

    public StringUtilJunit4TestCase(boolean expected, String... args) {
        mExpected = expected;
        mArgs = args;

    }

    @Test
    public void isStringNull() throws Exception {
        Assert.assertEquals(mExpected, StringUtil.isStringNull(mArgs));
    }

}

注意:必须要有对应参数顺序个构造函数,否则测试不通过

打包/套件测试 @Site.SuiteClasses

在Junit3中,我们定义一个TestSuite,在运行时,默认是运行在TestSuite中
而在junit4中需要使用注解进行声明
@RunWith(Suite.class) 指定测试环境
@Suite.SuiteClasses({MathUtilJunit4TestCase.class, StringUtilJunit4TestCase.class}) 指定包含测试的值

ackage com.czt.saisam.unittest.util.junit4;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

/**
 * Junit4
 *
 * @author byweichyang
 * @since
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({MathUtilJunit4TestCase.class, StringUtilJunit4TestCase.class})
public class Junit4TestSuite {

}

指定测试方法执行顺序 @FixMethodOrde

package com.czt.saisam.unittest.util.junit4;

import org.junit.Test;

/**
 * Created by weichyang on 2017/1/16.
 */

public class order {

    @Test
    public void a() {
        System.out.print("RUN A \n");
    }


    @Test
    public void b() {
        System.out.print("RUN b \n");
    }


    @Test
    public void c() {
        System.out.print("RUN c \n");
    }


    @Test
    public void d() {
        System.out.print("RUN d \n");
    }


    @Test
    public void e() {
        System.out.print("RUN e \n");
    }
}

默认情况

这里写图片描述

指定顺序常量有三种 可以一一测试,这里不进行赘述。

这里写图片描述

命令行运行测试

可能有些大神会选择命令行模式进行单元脚本执行,这里进行简单的介绍
通过运行 ./gradlew test 即可运行项目的单元测试用例

测试报告

生成必要的测试报告是很有必要的。
这里需要导入相应的包,默认的junit是不会产生测试报告的

也可以在 build.gradle 文件中重新制定测试报告的位置

android {
    testOptions {
        // 默认测试报告路径build/reports/androidTests/
        // 可以通过下面代码自定义测试路径
        resultsDir = "${project.buildDir}/testReport"
    }
}

参考资料

JUnit4用法详解 http://www.blogjava.net/jnbzwm/archive/2010/12/15/340801.html
参数化测试 http://blog.csdn.net/wangpeng047/article/details/9630203
Junit4总结 http://caizhitao.com/2015/07/20/android-test-use-junit4/
junit4进行参数化测试 https://blogs.oracle.com/jacobc/entry/parameterized_unit_tests_with_junit

测试源码地址: https://github.com/zhitaocai/UnitTest/tree/master

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灯塔@kuaidao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值