Java基础加强:JUnit 单元测试

作为一个 Java 开发人员,为了编写出优秀的代码,我们要既要懂得在编写时注意各种规范和逻辑,也要懂得如何对代码进行测试。

本篇文章就带领大家体验使用 JUnit 测试框架来对 Java 代码进行测试。

什么是单元测试

在软件行业,测试方法大致分为两大类:

  1. 黑盒测试。

    在黑盒测试中,测试人员不考虑程序内部结构和内部特性,不需要写代码。例如我对该程序输入测试后,黑盒测试随后只检查程序执行之后的输出的结果是否正确,着眼考虑于程序的外部结构

  2. 白盒测试。

    相对于黑盒测试,白盒测试需要测试人员关注程序内部功能实现的流程,需要写代码。白盒测试在输入测试后,用户可以看到程序中的内部结构,逻辑,算法…. 等。

可以看到,白盒测试对于测试人员的要求更高,但是相应地,白盒测试能够发现程序的更多细致的问题,所以白盒测试对于开发人员来说是一项不可或缺的技能。

在 Java 中,使用 JUnit 单元测试框架对代码测试就是一种白盒测试的方式,接下来,我们就来一步步开始了解和使用 JUnit 。

1. 初步使用 JUnit

对于使用 JUnit 单元测试来说,有以下几个步骤:

  • 定义一个测试类
  • 导入 JUnit 测试环境
  • 在测试类中定义一个可以独立运行的测试方法
  • 给方法加 @Test 注解

下面我使用一个用例来具体解析如何使用 JUnit 进行测试。

1.1 新建一个测试项目

打开 IDEA, 点击 File>New>Project,左边选择 Java ,选择好 JDK 的路径 ,点击 Next ,再点击 Next,将项目名命名为JunitTest,然后点击 FINISH ,创建项目。

src 目录下新建一个名为 junit 的包:鼠标右键单击src 目录,然后点击 New>Package,输入名字为 junit,点击 OK。

最后在 junit 包下新建我们想要测试的 Calculator 类:鼠标右键单击 junit 目录,然后点击 New>Java Class,输入名字为Calculator,点击OK。

此时我们的项目结构如下:

然后我们在Calculator类中定义两个方法用作被测试,代码如下:

package junit;

/**
* 计算器类
*/
public class Calculator {

/**
* 加法操作
* @param a
* @param b
* @return
*/

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

/**
* 减法操作
* @param a
* @param b
* @return
*/
public int substract (int a , int b) {
return a - b;
}

}

至此,项目基本框架建立完成。

1.2 新建测试类

上面我们已经建好了项目且编写好了被测试类的方法,若要对 Calculator 中的方法进行测试的话,还需要编写一个测试类。

对于开发人员来说,将测试代码和源代码分开是一个好习惯。对于测试类,我们在src 同级目录下新建test文件夹,专门用来放测试代码。接着在 IDEA里还要把这个 test 文件夹要设置成测试文件的根目录,右键选中 Mark Directory As - Test Sources Root

建好目录之后,我们在同样在 test 目录下新建一个与要测试的类相同的包 junit

同时在该包下新建类名为 CalculatorTest 的类。在编写代码的过程中,将测试类的包名和被测试的包名保持一致是一个好习惯

良好的命名往往具有锦上添花的作用。对于测试类的命名,我们推荐使用要测试的类名 + Test

此时项目结构如下:

1.3 在测试类中新建测试方法

打开 CalculatorTest 类,在里面输入以下内容:

package jnnit;

import junit.Calculator;

public class CalculatorTest {


/**
* 测试add方法
*/
public void testAdd(){
//1.创建计算器对象
Calculator ca = new Calculator();
//调用add方法
int result = ca.add(1,3);
System.out.println(result);
}
}

可以看到,我们定义了一个testAdd方法,用于对 Calculator 类中的 add 方法进行测试。

在这个方法中,我们首先是创建了一个计算器 Calculator 对象,然后调用它的 add 方法将 1,3 相加,最后输出相加的结果。

1.4 添加注解独立运行

但是,这样的方法并不能直接运行。Java 是完全面向对象的,要使用testAdd方法,我们必须先 new 一个对象出来,然后调用该方法,该方法才会被直接执行。

但是 JUnit 测试框架通过注解将该操作简化,我们只需在测试的方法上加上@Test 注解,就可以实现独立运行,下面是加上注解后的代码:

package jnnit;

import junit.Calculator;
import org.junit.Test;

public class CalculatorTest {


/**
* 测试add方法
*/
@Test
public void testAdd(){
//1.创建计算器对象
Calculator ca = new Calculator();
//调用add方法
int result = ca.add(1,3);
System.out.println(result);
}
}

第一次使用的话,注解会报红,这是没有导入包的原因,我们点击 @Test 注解,按键盘的 Alt+Enter 键:

选择第一个,然后 IDEA 就会自动加载该包。

加上注解后,我们可以看到在方法左边多了个绿色箭头,这代表该方法可以直接运行。在 main 方法上,左边也有同样的箭头,。

我们点击该箭头运行,出现如下结果:

可以看到,左边显示绿色提示,右边的控制台打印了我们输出的相加结果,表示测试成功。

当测试出现异常时,左边的提示会变成红色。

我们在测试时,都是通过是否出现提示红色来判断测试的程序段是否有问题,这就要求我们的测试代码在编写时要有一定的严谨性。

下面我来介绍增加测试代码严谨性的方法。

2. 使用断言

2.1 什么是断言?

断言就是对你的测试结果确定一个事先的肯定结果。在对这个测试结果做了断言之后,测试结果会与你事先希望的结果比较,结果一样,测试提示为绿色,结果不一样,测试提示为红色。

2.2 为什么使用断言?

在我们上面的测试中,我们调用Calculator 中的 add 方法,然后输出结果。

但是这样的测试存在很多弊端,也就是只通过结果无法判断程序的逻辑是否正确。

例如,我将Calculator 类中的add方法修改:

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

然后我们再次运行测试类的 testAdd 方法:

左边提示为绿色,没有发现问题。

我修改 add 方法的代码之后,其逻辑已经出现了问题,但是测试并没有出现问题,可以看出单纯通过打印输出结果无法判断程序是否正确

2.3 开始使用断言

JUnit 提供了一个类 Assert ,它有许多静态重载方法来定义断言:

修改之前的 testAdd 方法为使用断言的方式:

@Test
public void testAdd(){
//1.创建计算器对象
Calculator ca = new Calculator();
//2.调用add方法
int result = ca.add(1,2);
//System.out.println(result);
//3.使用断言
Assert.assertEquals(3,result);
}

assertEquals()接收两个值,前者为我们期望的值也就是断言的值,后者时测试的真正结果值,此时我们再次运行测试方法:

可以看到,左边并没有变为绿色,这里没显示红色是我主题的原因。但是我们可以通过右边看到,上面提示 Test failed ,即测试失败。

然后列出了原因 :

Expected :3 期望为3
Actual:-1 实际为-1

此时我们将Calculator 中的 add 方法修改回正常:

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

运行测试方法,测试成功:

3. 补充内容

使用@Test注解能够将方法设定为独立运行,但是,每次我们运行测试方法时,还是要 new 一个测试类的对象,只是这个操作由 JUnit 帮我们去完成了而已。

在大多数时候,这并不会有太大的影响。然而,对于一些重复类的逻辑,比如 IO操作,我们每一个测试方法都要先获得被测试类的对象才能调用它对应的方法,而这些逻辑基本在每个测试方法都是相同的,这样就会造成代码冗余。

为了减缓这种现象,我们一般会将这些操作都放在一个函数里面,在类被使用时首先就执行,使用结束后释放。

而 JUnit 也提供了这样的功能,通过@Before注解,指定 该方法在所有测试方法执行之前执行

通过 @After 注解,指定 该方法在所有测试方法之后执行

我们来试验一下,修改测试类的代码如下:

package jnnit;

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


public class CalculatorTest {
/**
* 在所有测试方法之前执行
* 一般用于数据和资源的初始化
*/
@Before
public void beforeMethod(){
System.out.println("Before");
}

/**
* 在所有测试方法之后执行
* 一般用于数据和资源的释放
*/
@After
public void afterMethod() {
System.out.println("After");
}


/**
* 测试add方法
*/
@Test
public void testAdd(){
//1.创建计算器对象
Calculator ca = new Calculator();
//2.调用add方法
int result = ca.add(1,2);
// System.out.println(result);
//3.使用断言
Assert.assertEquals(3,result);
}

}

然后我们运行测试方法:

可以看到打印的顺序和我们设想的一样。

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

  • @Test:把一个方法标记为测试方法
  • @Before:每一个测试方法执行前自动调用一次
  • @After:每一个测试方法执行完自动调用一次
  • @BeforeClass:所有测试方法执行前执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
  • @AfterClass:所有测试方法执行完执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
  • @Ignore:暂不执行该测试方法

参考链接

https://blog.csdn.net/dreamweaver_zhou/article/details/79850202