Junit4单元测试的基本用法(快速入门)

单元测试

哈哈哈,大佬就勿看啦!这个给纯小白看的!嘻嘻嘻嘻

单元测试可以帮助我们验证程序的逻辑是否正确、可以降低bug修复的成本、更有利于代码重构等等。所以,我们在写代码的时候,尽量保证单元测试的覆盖率。能力好的可以先写测试用例,再写功能代码(测试先行)。

添加依赖

1、在build gradle(Module:app) 的dependencies添加依赖

	testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

Caculate 类测试

1、创建一个Caculate类
在这里插入图片描述
里面先写四个方法,runadd(),runequal(),runmul(),runmul()

package com.example.testdemo;

public class Caculate {

    public int runadd(int a, int b){
        return a+b;
    }

    public int runequal(int a, int b){
        return a-b;
    }

    public int runmul(int a, int b){
        return a*b;
    }

    public int rundivide(int a,int b) throws Exception{
        if(b == 0){
            throw new Exception("被除数不能为0");
        }else{
            return a/b;
        }
    }
}

2、右键Caculate类名,点Go To Test
在这里插入图片描述
选择JUnit4,因为Android studio 已经帮我们集成好了,下面Member中的四个方法要打勾,生成需要测试的代码,点击ok
在这里插入图片描述

这时可以看到测试方法已经构建完了,我们开始在方法体中写具体的实现方法了。这里我写了一个CaculateTest构造方法,后面要用到滴,然后还写了个@Ignore,这后面也要用到先写上来了。

package com.example.testdemo;

import android.util.Log;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;

public class CaculateTest {

    public CaculateTest() {
        System.out.println("构造方法");
    }

    @BeforeClass
    public static void setUpBeforeClass() {
        System.out.println("BeforeClass");
    }

    @AfterClass
    public static void tearDownAfterClass() {
        System.out.println("AfterClass");
    }

    @Before
    public void setUp() throws Exception {
        System.out.println("Before");
    }

    @After
    public void tearDown() throws Exception {
        System.out.println("After");
    }

    @Test
    public void runadd() {
        System.out.println("Test__runadd");
        assertEquals(7, new Caculate().runadd(2,3));
//        Caculate caculate =new Caculate();
//        int r= caculate.runadd(2,3);
//        assertEquals(r,5);

        //可以把这三行代码写成一段
        //assertEquals(5, new Claculate().add(2, 3));
        /**assertEquals这个方法是一个断言方法
         第一个参数表示预期的结果
         第二个参数表示程序的执行结果
         当预期结果与执行结果是一致的时候,则表示单元测试成功
         **/
    }

    @Test
    public void runequal() {
        System.out.println("Test__runequal");
        Caculate caculate =new Caculate();
        int r= caculate.runadd(2,3);
        assertEquals(r,5);
    }

    @Test
    public void runmul(){
        System.out.println("Test__runmul");
        assertEquals(20,new Caculate().runmul(4,5));
    }

    @Ignore
    @Test(expected = Exception.class)
    public void rundivide() throws Exception{
        System.out.println("Test__rundivide");
        assertEquals(2,new Caculate().rundivide(4,2));
        fail("被除数不能为0");
    }
}

上面我把2+3的结果故意写错,写成7,这样在运行test时,它就会报错,更方便我们了解。

执行Test代码,可以执行所有代码,在CaculateTest右键,run Test
在这里插入图片描述

执行结果如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200721105838827.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTU1MjQ3NQ==,size_16,color_FFFFFF,t_70

如果我们把7,改为5,那么运行就不会报错了,运行成功结果如下:
在这里插入图片描述

执行顺序

JUnit4利用JDK5的新特性Annotation,使用注解来定义测试规则。这里讲一下以下几个常用的注解:

  • @Test:把一个方法标记为测试方法
    • @Test(excepted=xx.class): xx.class表示异常类,表示测试的方法抛出此异常时,认为是正常的测试通过的
    • @Test(timeout=毫秒数) :测试方法执行时间是否符合预期
  • @Before:每一个测试方法执行前自动调用一次
  • @After:每一个测试方法执行完自动调用一次
  • @BeforeClass:所有测试方法执行前执行一次,在测试类还没有实例化就已经被加载,所以用static修饰,通常进行一些资源的加载。
  • @AfterClass:所有测试方法执行完执行一次,在测试类还没有实例化就已经被加载,所以用static修饰,通常用来释放资源。
  • @Ignore:暂不执行该测试方法
  • @RunWith:可以更改测试运行器org.junit.runner.Runner
  • Parameters:参数化注解
package com.example.testdemo;

import android.util.Log;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;

public class CaculateTest {

    public CaculateTest() {
        System.out.println("构造方法");
    }

    @BeforeClass
    public static void setUpBeforeClass() {
        System.out.println("BeforeClass");
    }

    @AfterClass
    public static void tearDownAfterClass() {
        System.out.println("AfterClass");
    }

    @Before
    public void setUp() throws Exception {
        System.out.println("Before");
    }

    @After
    public void tearDown() throws Exception {
        System.out.println("After");
    }

    @Test
    public void runadd() {
        System.out.println("Test__runadd");
        assertEquals(5, new Caculate().runadd(2,3));
//        Caculate caculate =new Caculate();
//        int r= caculate.runadd(2,3);
//        assertEquals(r,5);

        //可以把这三行代码写成一段
        //assertEquals(5, new Claculate().add(2, 3));
        /**assertEquals这个方法是一个断言方法
         第一个参数表示预期的结果
         第二个参数表示程序的执行结果
         当预期结果与执行结果是一致的时候,则表示单元测试成功
         **/
    }

    @Test
    public void runequal() {
        System.out.println("Test__runequal");
        Caculate caculate =new Caculate();
        int r= caculate.runadd(2,3);
        assertEquals(r,5);
    }

    @Test
    public void runmul(){
        System.out.println("Test__runmul");
        assertEquals(20,new Caculate().runmul(4,5));
    }

    @Ignore
    @Test(expected = Exception.class)
    public void rundivide() throws Exception{
        System.out.println("Test__rundivide");
        assertEquals(2,new Caculate().rundivide(4,2));
        fail("被除数不能为0");
    }
}

运行结果
在这里插入图片描述

  • @BeforeClass和@AfterClass在类被实例化前(构造方法执行前)就被调用了,而且只执行一次,通常用来初始化和关闭资源。

  • @Before和@After和在每个@Test执行前后都会被执行一次。

  • @Test标记一个方法为测试方法没什么好说的,被@Ignore标记的测试方法不会被执行,例如这个模块还没完成或者现在想测试别的不想测试这一块。

  • 以上有一个问题,构造方法居然被执行了三次次。所以我这里要说明一下,JUnit4为了保证每个测试方法都是单元测试,是独立的互不影响。所以每个测试方法执行前都会重新实例化测试类。

Test的两个属性——细讲

我们都能看到上面Test中有两个属性,一个异常类,还有个检测时间逾期问题,那么下面通过一个例子带大家详细了解一下。

我们可以看到在Caculate类里面有一个rundivide()方法,执行除法,运算时时不允许被除数为0的,这时候就要抛出异常

 public int rundivide(int a,int b) throws Exception{
        if(b == 0){
            throw new Exception("被除数不能为0");
        }else{
            return a/b;
        }
    }

然后去CaculateTest测试类中,将@Ignore删除

   @Test(expected = Exception.class)
    public void rundivide() throws Exception{
        System.out.println("Test__rundivide");
        assertEquals(2,new Caculate().rundivide(4,2));
        fail("被除数参数为0没有抛出异常");
    }

我们将被除数设置为不为0的数,执行结果如下
在这里插入图片描述
将参数设为0,结果如下
在这里插入图片描述
这个方法就是(expected = Exception.class)和fail(“被除数参数为0没有抛出异常”);之间的配合。就是这个测试方法会检查是否抛出Exception异常(当然也可以检测是否抛出其它异常),如果抛出了异常那么测试通过(因为你的预期就是传进负数会抛出异常)。没有抛出异常则测试不通过执行fail(“被除数参数为0没有抛出异常”);

然后说下timeout属性,这个是用来测试性能的,就是测试一个方法能不能在规定时间内完成。
在Caculate类里面添加一个新的方法sort(),创建一个数组排序的方法,用的是冒泡排序。

public void sort(int[] arr) {
        //冒泡排序
        for (int i = 0; i < arr.length - 1; i++) { //控制比较轮数
            for (int j = 0; j < arr.length - i - 1; j++) { //控制每轮的两两比较次数
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

接下来在CaculateTest类中添加测试方法,随机生成一个长度为50000的数组然后测试排序所用时间。timeout的值为2000,单位和毫秒,也就是说超出2秒将视为测试不通过。

   @Test(timeout = 2000)
    public void runSort() throws Exception {
        int[] arr = new int[50000]; //数组长度为50000
        int arrLength = arr.length;
        //随机生成数组元素
        Random r = new Random();
        for (int i = 0; i < arrLength; i++) {
            arr[i] = r.nextInt(arrLength);
        }
        new Caculate().sort(arr);
    }

运行CaculateTest,测试结果不通过,
在这里插入图片描述
那接下来,该怎么办呢?我想大家应该都知道了吧,将sort()方法进行改进,用一种快速的排序方法——快速排序

 public void sort(int[] arr) {
        //快速排序
        if (arr.length <= 1) {
            return;
        } else {
            partition(arr, 0, arr.length - 1);
        }

    }

    static void partition(int[] arr, int left, int right) {
        int i = left;
        int j = right;
        int pivotKey = arr[left]; //基准数

        while (i < j) {

            while (i < j && arr[j] >= pivotKey) {
                j--;
            }

            while (i < j && arr[i] <= pivotKey) {
                i++;
            }

            if (i < j) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        if (i != left) {
            arr[left] = arr[i];
            arr[i] = pivotKey;
        }

        if (i - left > 1) {
            partition(arr, left, i - 1);
        }

        if (right - j > 1) {
            partition(arr, j + 1, right);
        }
    }

再运行一次,就完美通过啦!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45552475/article/details/107481416