Java单元测试工具 Junit4入门使用教程
阅读原文时间:2021年04月23日阅读:1

文章目录

1.序言

JUnit是一个开源的Java单元测试框架,它由Kent Beck和Erich Gamma建立,JUnit4框架中运用了Java5的Annotation接口简化测试用例的编写,JUnit测试框架将测试更加便捷和容易,编写测试代码也是简单、明了,功能强大。

2.前期准备

工具准备:
(1)IntelliJ IDEA(用于创建Java 项目)
(2)安装配置好JDK 1.8
(3)安装配置好Maven

3.创建工程,添加包依赖

  • 使用Intellij IDEA 快速新建一个Maven构建的工程
  • 在项目工程文件pom.xml中添加Junit4、hamcrest-core、hamcrest-library库依赖

4.JUnit代码实战演示

4.1 实战第一步:

新建编写一个Calculator类,这是一个能够简单实现加减乘除、平方、开方的计算器类,然后对这些功能进行单元测试,这个类并不是很完美,预先故意保留了一些Bug用于演示,代码详情及注释如下:

public class Calculator {
    private static int result; // 静态变量,用于存储运行结果
    public void add(int n) {
        result = result + n;  //加法算法
    }
    public void substract(int n) {
        result = result - 1;  //预留Bug: 正确的应该是 result =result-n
    }
    public void multiply(int n) {
        // 此方法暂未实现,用于备用
    }         
    public void divide(int n) {
        result = result / n;  
    }
    public void square(int n) {
        result = n * n;
    }
    public void squareRoot(int n) {
        for (; ;) ;            //预留Bug : 死循环
    }
    public void clear() {     // 将result结果清零
        result = 0;
    }
    public int getResult() {  //获取最终result的值
        return result;
    }
}

4.2 实战第二步

生成JUnit测试框架:用右键点击类Calculator弹出菜单,并选择Go to–>Test,勾选如下图所示:

点击后选择Create Test,系统会自动列出你这个类中包含的方法,选择你要进行测试的方法。此例中,我们将对“加、减、乘、除”四个方法进行测试,如下图:

之后系统会自动生成一个新类CalculatorTest,同时自动生成一些空的测试用例,接下来我们要修改我们这些测试用例,修改后结果如下:

package com.wmin.JUnit4TestDemo;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.*;

public class CalculatorTest {
    private static Calculator calculator=new Calculator();
    @Before
    public void setUp() throws Exception {
        calculator.clear();
    }
    @Test     //@Test表示自己要测试的方法
    public void testAdd() {
        calculator.add(2);
        calculator.add(3);
        System.out.println("输出加法运算结果:"+calculator.getResult());
        assertEquals(5, calculator.getResult());
    }

    @Test
    public void testSubstract() {
        calculator.add(10);
        calculator.substract(2);
        System.out.println("输出加法与减法混合后的运算结果:"+calculator.getResult());
        assertEquals(8, calculator.getResult());
    }
    @Ignore("Multiply() Not yet implemented")
    @Test
    public void testMultiply() {
        System.out.println("乘法运算方法未实现!");  //被忽略的方法将不会执行
    }

    @Test
    public void testDivide() {
        calculator.add(8);
        calculator.divide(2);
        System.out.println("输出加法与除法混合后的运算结果:"+calculator.getResult());
        assertEquals(4, calculator.getResult());
    }
}

4.3 实战第三步

运行测试代码,右键点击CalculatorTest测试类,选择“Run As–>Junit Test”,运行结果图如下;
控制台Junit窗口结果如下:

4.4 实战结果分析

(1)观察步骤4.3中的JUnit运行结果,右上角红色进度条表示表示发现出现错误,表示存在测试案例未通过,如下图,执行了4个测试案例,一个失败,一个忽略;
(2)JUnit输出控制窗口左侧记录每个测试案例的执行结果以及执行消耗时间,右侧窗口
显示测试未通过的测试案例;
(3)测试未通过案例分析:点击不通过案例,输出结果如下:

输出加法与减法混合后的运算结果:9
java.lang.AssertionError: 
Expected :8
Actual   :9

表示测试案例期望值为“8”,但实际运行结果为“9”,测试未通过;
分析testSubstract() 方法:

 @Test
    public void testSubstract() {
        calculator.add(10);
        calculator.substract(2);
        System.out.println("输出加法与减法混合后的运算结果:"+calculator.getResult());
        assertEquals(8, calculator.getResult());
}

其中Calculator对象分别调用add(10)和sunbstract(2)方法,预期结果result执行这两个方法后为8,实际因为substract方法存在Bug,导致实际结果在执行减法后result值为9,Bug如下:

 public void substract(int n) {
        result = result - 1;  //预留Bug: 正确的应该是 result =result-n
 }

5.JUnit4使用教程中级篇

通过JUnit代码实战演练,我们可认识到JUnit 4的基本用法,下面我来熟悉一下第四章节中使用到的Junit4框架及关键字等知识

5.1 引入必要的包

在测试类中用到了JUnit4框架,自然要把相应地Package包含进来。最主要地一个Package就是org.junit.*。把它包含进来之后,绝大部分功能就有了。还有一句话也非常地重要“import static org.junit.Assert.*;”,我们在测试的时候使用的一系列assertEquals方法就来自这个包。大家注意一下,这是一个静态包含(static),是JDK5中新增添的一个功能。也就是说,assertEquals是Assert类中的一系列的静态方法,一般的使用方式是Assert. assertEquals(),但是使用了静态包含后,前面的类名就可以省略了,使用起来更加的方便。

5.2 测试类的声明

大家注意到,我们的测试类是一个独立的类,没有任何父类。测试类的名字也可以任意命名,没有任何局限性。所以我们不能通过类的声明来判断它是不是一个测试类,它与普通类的区别在于它内部的方法的声明,我们接着会讲到。

5.3 创建一个待测试的对象

你要测试哪个类,那么你首先就要创建一个该类的对象。正如上一篇文章中的代码:

private static Calculator calculator = new Calculator();

为了测试Calculator类,我们必须创建一个calculator对象。

5.4 测试方法的声明

在测试类中,并不是每一个方法都是用于测试的,你必须使用“标注”来明确表明哪些是测试方法。“标注”也是JDK5的一个新特性,用在此处非常恰当。我们可以看到,在某些方法的前有@Before、@Test、@Ignore等字样,这些就是标注,以一个“@”作为开头。这些标注都是JUnit4自定义的,熟练掌握这些标注的含义非常重要。

5.5 编写一个简单的测试方法

首先,你要在方法的前面使用@Test标注,以表明这是一个测试方法。对于方法的声明也有如下要求:名字可以随便取,没有任何限制,但是返回值必须为void,而且不能有任何参数。如果违反这些规定,会在运行时抛出一个异常。至于方法内该写些什么,那就要看你需要测试些什么了

例如下方的测试案例

public void testAdd() {
      calculator.add(2);
      calculator.add(3);
      System.out.println("输出加法运算结果:"+calculator.getResult());
      assertEquals(5, calculator.getResult());
}

想要测试计算加法的方法是否正确,调用两次add方法,result初始值为0,最终结果为5,如果预期结果与实际结果一致,就通过,反之,测试不通过。
assertEquals(5, calculator.getResult())方法中,第一个参数填写期望结果值,第二个参数填写实际结果值,JUnit会自动比对期望结果与实际结果是否相等并反馈给用户。

5.6 忽略未完成的测试方法

如果一个方法的测试用例已经写好,但是方法体并未实现,在执行测试的时候,一定会失败,但这并不是真正的失败,JUnit提供了一种方法来区别他们,那就是在这种测试函数的前面加上@Ignore标注,表示此方法暂时不参与测试,挂起处理。

5.7 Fixture用法

Fixture的含义就是“在某些阶段必然被调用的代码”。比如我们上面的测试,由于只声明了一个Calculator对象,他的初始值是0,但是测试完加法操作后,他的值就不是0了;接下来测试减法操作,就必然要考虑上次加法操作的结果,因此我们需要再所有测试用例执行前清空其他干扰,@Before的作用就是使所有测试用例执行前,都必须执行的一个“前置”步骤,同理还有@After,表示所有测试用例执行完毕后都要执行@After中的收尾工作。

6.JUnit4 使用教程高级篇

6.1 测试函数执行效率

6.2 高级Fixture

第5.7章节讲述@Before和@After方法,但是@Before和@After是在测试类中每一个测试用例方法之前和之后都要执行一篇,遇到复杂的程序或耗时较长的程序时,比较费时间,JUnit提供了@BeforeClass和@AfterClass方法,表示每个测试类只在初始化时执行@BeforeClass当中的内容,当所有测试用例执行完毕之后,再执行@AfterClass方法
例如:
@BeforeClass
public void setUp() throws Exception { //异常判断
calculator.clear();
}
则表示方法在类开始时执行一次,后续每个TestCase执行时,不在执行这个方法。
注意:每个测试类只能有一个方法被标注为@BeforeClass或@AfterClass,并且该方法必须是 Public和Static的

6.3 限时测试

如果一个测试案例执行时间较长,或者出现死循环不停执行时,我们需要控制测试案例执行时间,只需在@Test中添加时间参数,Timeout 参数表明了你要设定的时间,单位为毫秒
例如下面高级测试类AdvancedTest.java中

package JunitTest;

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class AdvancedTest {

    private static Calculator calculator = new Calculator(); //创建一个Calculator对象

    @Before
    public void setUp() throws Exception {
        calculator.clear();
    }

    @Test(timeout=2000)
    public void testSquareRoot() {
        //fail("Not yet implemented");
        calculator.squareRoot(4);
        assertEquals(2, calculator.getResult());
    }
}

因原始类Calculater中的SquareRoot()方法存在无限循环缺陷,设置testSquareRoot()方法超时参数后,方法执行2秒后即停止,控制台输出结果如下:

6.4 异常测试

JAVA中的异常处理也是一个重点,因此你经常会编写一些需要抛出异常的函数。那么,如果你觉得一个函数应该抛出异常,这当然是Bug ,并且JUnit也考虑到了这一点,来帮助我们找到这种 Bug 。例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除以0异常”类似的提示。因此,我们很有必要对这些进行测试。

@Test(expected=ArithmeticException.class )
     //@Test
    public   void  divideByZero()  {

    calculator.divide( 0 ); 

   } 

6.5 Runner( 运行器 )

当你把测试代码提交给JUnit框架后,框架如何来运行你的代码呢?答案就是Runner 。在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。

运行器使用@RunWith标注,并且把你所指定的 Runner 作为参数传递给它。另外一个要注意的是, @RunWith 是用来修饰类的,而不是用来修饰函数或者方法的。如果不指定Runner,则会使用系统默认的TestClassRunner,例如下方的两个代码效果是一样的
public class CalculatorTest {

}

@RunWith(TestClassRunner. class )
public class CalculatorTest {

}

6.6 参数化测试

在进行单元测试案例设计时,我们对同一方法的参数取值可能会存在多种,例如Calculater.java中求平法这个方法,参数取值为整数类型,取值可能有正数、负数、0三种情况,测试时我们可以写三个测试案例,三个测试方法对这个求平方的方法进行校验,如下所示:
public void square1() {

}
public void square2() {

}
public void square3() {

}
如果要测试的方法中,参数取值有7、8种甚至十几种,这种方式写测试案例显示不行,为了简化类似的测试, JUnit4 提出了“参数化测试”的概念,只写一个测试函数方法,把这若干种情况作为参数传递进去,一次性的完成所有输入参数的测试。代码如下:

package JunitTest;

import  static  org.junit.Assert.assertEquals;
import  org.junit.Test;
import  org.junit.runner.RunWith;
import  org.junit.runners.Parameterized;
import  org.junit.runners.Parameterized.Parameters;

import  java.util.Arrays;
import  java.util.Collection;

@RunWith(Parameterized. class )

public class SquareTest{

     private static Calculator calculator  =  new  Calculator();

     private  int param;
     private  int result;

    @Parameters
     public  static Collection data(){
         return  Arrays.asList(new Object[][] {
                 {2,  4},
                 {0,  0},
                 {-3,-9},
         });
    } 

// 构造函数,对变量进行初始化 
     public  SquareTest(int param,  int result){
         this .param  = param;
         this .result = result;
    } 

    @Test
     public void square(){
        calculator.square(param);
        assertEquals(result, calculator.getResult());
    } 
} 

参数化测试操作步骤及注意事项:
(1)参数化测试时,你要为这种测试专门生成一个新的类,而不能与其他测试共用同一个类;
(2)然后,你要为这个类指定一个Runner,而不能使用默认的Runner了,因为特殊的功能要用特殊的Runner嘛。@RunWith(Parameterized.class)这条语句就是为这个类指定了一个ParameterizedRunner。
(3)定义一个待测试的类,并且定义两个变量,一个用于存放参数,一个用于存放期待的结果。接下来,定义测试数据的集合,也就是上述的data()方法,该方法可以任意命名,但是必须使用@Parameters标注进行修饰,注意其中的数据,是一个二维数组,数据两两一组,每组中的这两个数据,一个是参数,一个是你预期的结果,比如我们的第一组{2, 4},2就是参数,4就是预期的结果。这两个数据的顺序前后任意,但是下一步构造方法参数要跟这个顺序匹配;
(4)构造函数,其功能就是对先前定义的两个参数进行初始化。 在这里你可要注意一下参数的顺序了,要和上面的数据集合的顺序保持一致。如果前面的顺序是{参数,期待的结果},那么你构造函数的顺序也要是“构造函数(参数, 期待的结果)”,反之亦然。最后就是写一个简单的测试案例。

6.7 打包测试

实际项目中,我们会写出很多很多个测试类,如果这些测试类必须一个一个的执行,也是比较麻烦的事情;JUni为我们提供了打包测试的功能,将所有需要运行的测试类集中起来,一次性的运行完毕,大大的方便了我们的测试工作。具体代码如下:

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

@RunWith(Suite. class )
@Suite.SuiteClasses( {
        CalculatorTest. class ,
        SquareTest. class 
        } )
public   class  AllCalculatorTests  {
  .......
} 

注意事项:
(1)打包测试需要一个特殊的Runner,在@RunWith标注传递一个参数Suite.class;
(2)同时,我们还需要另外一个标注@Suite.SuiteClasses ,来表明这个类是一个打包测试类,我们把需要打包的类作为参数传递给该标注就可以了,如上述例子中,将CalculatorTest和SquareTest两个类打包传递过去。
(3)打包测试类任意取名就行,类里面的内容为空即可。

7.相关源码

https://github.com/AnnWu/JUnit4TestDemo

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章