Java生态中,Junit是使用最广泛的单元测试框架,JUnit能和Eclipse等主流集成开发环境整合,点击一个按钮即可运行测试,此外也能和Maven、Gradle等构建工具整合,在构建时运行单元测试并生成测试报告,功能非常强大。
在早些年间Junit4比较流行,但随着时间的推移目前JUnit5使用更为广泛,新版本SpringBoot的单元测试模块也是基于JUnit5,Junit4和JUnit5在使用上API稍有区别,JUnit5扩展了更多强大的功能,不过基本用法是一致的。
使用JUnit5的基础功能需要2个模块,Junit Launcher
定义了用于在开发平台上运行测试框架的API;Junit Jupiter
定义了Junit注解和具体的TestEngine实现。
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
JUnit Vintage
主要用于兼容JUnit4或其它单元测试框架的历史遗留代码,如果不涉及可以不引入。
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
JUnit Jupiter Params
是一个可选的扩展模块,能够实现参数化测试,我们可以用CSV等方式加载测试用例所需的数据,这里不展开介绍,具体可以参考文档。参数化测试因为包含测试用例读取的逻辑,因此相对更复杂一些,过度设计的测试驱动开发是条邪路,这里只建议在适合的场景使用合适的方案。
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
我们这里以Maven工程为例,在src/test/java
文件夹下创建单元测试代码,建立的文件目录如下。
我们这里使用@Test
注解标注单元测试方法。
package com.gacfox.demo;
import org.junit.jupiter.api.Test;
public class DemoTest {
@Test
public void testA() {
System.out.println("Demo test");
}
}
运行时,我们直接在单元测试方法上点击右键,并以JUnit Test
方式运行;也可以直接在测试类上运行测试,此时会执行测试类中的所有单元测试方法。
运行完成后,相关的结果会显示在界面中。
如果由于某些原因运行失败,如图所示会显示相关信息。
下面我们介绍JUnit5中的API。
我们先看最基本的几个注解:
@BeforeAll
:在所有测试方法前运行,必须是静态方法@AfterAll
:在所有测试方法后运行,必须是静态方法@BeforeEach
:在测试类中每一个测试方法前运行@AfterEach
:在测试类中每一个测试方法后运行@Test
:标注为测试方法,可以在IDE或Maven等环境中触发单元测试执行package com.gacfox.demo;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class DemoTest {
@BeforeAll
public static void testBeforeAll() {
System.out.println("before all");
}
@AfterAll
public static void testAfterAll() {
System.out.println("after all");
}
@BeforeEach
public void testBeforeEach() {
System.out.println("before each");
}
@AfterEach
public void testAfterEach() {
System.out.println("after each");
}
@Test
public void testA() {
System.out.println("testA");
}
@Test
public void testB() {
System.out.println("testB");
}
}
运行整个测试类的执行结果:
before all
before each
testA
after each
before each
testB
after each
after all
JUnit5提供了一个新的Assertions
断言类,其中包括了实现断言的静态方法,断言如果未实现,就会抛出运行时异常,抛出异常则意味着测试用例执行失败,测试未通过。
常用的断言包括:
assertEquals()
和assertNotEquals()
:根据对象equals()
判断两个对象是否相等。assertTrue()
和assertFalse()
:根据==
运算符判断两个对象是否为同一个对象(基本类型为判断是否相等)。assertNull()
和assertNotNull()
:判断参数对象是否为null
。除此之外,JUnit还提供了很多实用的断言方法,这里就不全部列举了。使用断言的例子代码如下:
package com.gacfox.demo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class DemoTest {
@Test
public void testA() {
String a = new String("abc");
String b = new String("abc");
String c = null;
Assertions.assertEquals(a, b);
Assertions.assertFalse(a == b);
Assertions.assertNull(c);
}
}
上面测试用例可以运行通过。
除了@Test
注解,我们也可以使用@RepeatedTest
标注测试方法入口,它支持一个int
类型参数,能够实现多次重复执行一个测试方法。下面例子代码中,测试方法testA()
会执行3次。
package com.gacfox.demo;
import org.junit.jupiter.api.RepeatedTest;
public class DemoTest {
@RepeatedTest(3)
public void testA() {
System.out.println("重复测试");
}
}
@DisplayName
可以用来标注单元测试类和方法的名字,支持JUnit5的IDE会以标注的名字显示测试结果。
package com.gacfox.demo;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("我的第一个测试类")
public class DemoTest {
@Test
@DisplayName("我的第一个测试方法")
public void testA() {
System.out.println("testA");
}
}