1,简介
主要由三个部分组成:
- JUnit Platform
- JUnit Jupiter
- JUnit Vintage
maven依赖:
<dependencies>
<dependency>
<!-- JUnit5 新的编程和扩展模型 -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- maven 运行的依赖插件 -->
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<!-- 无需使用此插件,容易版本冲突,加载不起来 -->
<!-- <plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin> -->
</plugins>
</build>
2,命名规则
-
单元测试代码文件
- 默认写在工程目录:
src/test/java
- 不允许写在业务代码目录下(
src/main/java
是业务代码目录)
- 默认写在工程目录:
-
测试资源文件
- 默认写在资源目录:
src/test/resources
*文件默认命名规则:以Test开头或者以Test结尾的java文件 - 也可以使用 surefire 插件自定义命名规则,需要导入maven依赖:
- 默认写在资源目录:
-
使用maven依赖配置的文件,默认Test开头或者结尾的规则就会失效
<plugin>
<!-- 某些版本需要添加groupId,否则报错 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<includes>
<include>**/*Hogwarts*.java</include>
<include>**/*TestCase.java</include>
</includes>
</configuration>
</plugin>
3,测试用例结构
-
使用 @Test 注解表示为测试方法 ——如果不加@Test标识,这条用测不会被执行
-
使用 @DisplayName 定义别名 —— 测试用例增多,为了方便识别每个测试用例
-
使用 Assertions 类的断言方法进行测试用例的断言
4,常用注解
-
@Test 注解表示为测试方法
-
@DisplayName 定义别名
-
@BeforeEach 在每个用例执行前都会执行这一条用例
-
@AfterEach 在每个用例执行后都会执行这一条用例
-
@BeforeAll 在所有用例执行前会执行这一条,被装饰的方法必须加上static
-
@AfterAll 在所有用例执行后会执行这一条,被装饰的方法必须加上static
-
@Tag 为测试类或者方法添加标签
-
@RepeatedTest(n) 表示需要重复执行的用例,参数n表示需要再重复执行n次
-
@Order(n) 定义执行顺序,需要在测试类上打上@TestMethodOrder(MethodOrderer.OrderAnnotation.class)注解
-
@Disabled 会跳过这条用例不执行
package JUnit5Demo;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestAssertion {
@BeforeEach
void fn(){
System.out.println("在每个用例前都会执行");
}
@AfterEach
void after(){
System.out.println("在每个用例后都会执行");
}
@BeforeAll
static void ball(){
System.out.println("在所有用例前只执行一次=================");
}
@AfterAll
static void afterall(){
System.out.println("在所有用例后只执行一次******************");
}
@Test
@DisplayName("testAssertion用例")
@Tag("这是一条主用例")
void testAseertion(){
System.out.println("Hogwarts~");
// 利用断言,判断方法的实际执行结果和预期结果是否一致
// 第一个参数表示预期结果,第二个参数表示实际结果。
assertEquals(2,1+1);
// assertEquals(2,1+2); //结果为false时会报错
}
@Test
@DisplayName("testOne用例")
@RepeatedTest(3) //@RepeatedTest注解表示需要重复执行的用例,参数3表示需要再重复执行3次
void testOne(){
System.out.println("执行testOne用例");
}
@Test
@DisplayName("testTwo用例")
@Order(1) //定义执行顺序,需要在测试类上打上@TestMethodOrder(MethodOrderer.OrderAnnotation.class)注解
void testTwo(){
System.out.println("执行testTwo用例");
}
@Test
@Disabled //@Disabled注解,会跳过这条用例不执行
void test01(){
System.out.println("执行test01用例");
}
}
5,测试用例断言
断言有两种方法:
- 内置断言方法
- 必修
- assertEquals
- assertTrue
- assertAll
- 选修
- assertNotNull
- assertTimeout
- assertThrows
- 第三方库断言
- Hamcrest
- AssertJ
- Truth
package JUnit5Demo;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import static java.lang.Thread.sleep;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertAll;
public class AssertTest {
//JUnit5有两种断言方法:内置断言方法Assertion和第三方库断言
@Test
void testEquals(){
assertEquals(2,1+1);
// assertEquals(2,1+2);
//断言对象是表达式
assertTrue(3 > 1);
//断言对象是布尔类型
assertTrue(true);
// 断言对象如果不是null则返回true
assertNotNull("hog");
// 断言对象如果是null则返回false,报错
// assertNotNull(null);
}
@Test
void assertAllDemo(){
//如果同一个方法有多个断言对象,其中一个断言失败报错,则不会再往后执行其他的断言,这时候需要用assertAll()方法来进行断言
//assertAll()分组断言,也叫软断言,这种断言方式即使中间报错,也会执行完所有断言方法
assertAll("分组断言",
() ->assertEquals(2,1+1),
() ->assertEquals(2,1+3),
() -> assertTrue(3 >1));
}
@Test
void timeoutTest(){
//超时断言,它只会比较方法内地2个参数值是否小于Duration.ofSeconds()的值,小于则为true,大于则会fasle
assertTimeout(Duration.ofSeconds(3), () ->sleep(2000));
}
void fun(int a, int b){
System.out.println(a/b);
}
@Test
void exceptTest(){
//异常断言,抛了异常才是true,不抛异常就会断言失败报错
assertThrows(ArithmeticException.class, () ->fun(1,0));
}
}
6,测试框架结构
测试类之间的继承关系:
-
SubTest类继承自BaseTest类,如果SubTest类的方法与BaseTest类一样,则重写了父类方法,执行用例只会打印子类的用例结果
-
SubTest类的方法与BaseTest类不一样,执行BaseTest类,会打印BaseTest类和SubTest类的用例执行结果
-
执行顺序:父类优先级高于子类,先开始的后结束
-
beforeAllA > beforeAllB > beforeEachA > beforeEachB > method1,2,3,4 > afterEachB > afterEachA > afterAllB > afterAllA
-
JUnit5注解的继承关系,详细可以参考官网文档:https://junit.org/JUnit5/docs/current/user-guide/
package frameDemo;
import org.junit.jupiter.api.*;
public class BaseTest {
@BeforeAll
public static void beforeAllA(){
System.out.println("执行BaseTest —— beforeAllA !");
}
@AfterAll
public static void afterAllA(){
System.out.println("执行BaseTest —— afterAllA !");
}
@BeforeEach
public void beforeEachA(){
System.out.println("执行BaseTest —— beforeEachA !");
}
@AfterEach
public void afterEachA(){
System.out.println("执行BaseTest —— afterEachA !");
}
@Test
public void method1(){
System.out.println("执行BaseTest —— method1 !");
}
@Test
public void method2(){
System.out.println("执行BaseTest —— method2 !");
}
}
SubTest类继承自BaseTest:
package frameDemo;
import org.junit.jupiter.api.*;
public class SubTest extends BaseTest{
@BeforeAll
public static void beforeAllB(){
System.out.println("执行SubTest —— beforeAllB !");
}
@AfterAll
public static void afterAllB(){
System.out.println("执行SubTest —— afterAllB !");
}
@BeforeEach
public void beforeEachB(){
System.out.println("执行SubTest —— beforeEachB !");
}
@AfterEach
public void afterEachB(){
System.out.println("执行SubTest —— afterEachB !");
}
@Test
public void method3(){
System.out.println("执行SubTest —— method3 !");
}
@Test
public void method4(){
System.out.println("执行SubTest —— method4 !");
}
}
7,测试用例调度与运行
-
maven 对应有默认配置:
maven-surefire-plugin
-
当需要修改一些测试相关执行策略的时候,就需要重新配置这个插件
-
执行测试用例的两种方式
-
命令行执行 :
mvn test ——执行当前项目下的所有测试
mvn test -Dtest=包名.类名 ——执行单个测试类
mvn test -Dtest=包名.类名#方法名 ——执行单个测试方法
mvn test -Dtest=包名.类名#方法名1+方法名2 ——执行多个测试方法
mvn test -Dtest=包名.类名1,包名.类名2,… ——执行同一个包下面的测试类
mvn test -Dtest=包名1.类名1,包名2.类名2,… ——执行不同包下面的测试类
mvn test -Dtest=“包名.*Test” ——正则匹配执行多个测试类 -
配置文件执行: 包名之间用/区分, 对应写法有3种方式,用哪种都可以
-
执行单个测试类的配置:
<!-- 可以是类名.java -->
<include>包名/类名.java</include>
<!-- 可以直接是类名 -->
<include>包名/类名</include>
<!-- 可以是类名.class -->
<include>包名/类名.class</include>
执行同一个包下面的测试类:
<!-- 可以是包名.java -->
<include>包名1/*.java</include>
<!-- 可以直接是包名 -->
<include>包名1/*</include>
<!-- 可以是包名.class -->
<include>包名1/*.class</include>
执行不同包下面的测试类:
<!-- 可以是类名.java -->
<include>包名1/类名1.java</include>
<include>包名2/类名2.java</include>
...
<!-- 可以直接是类名 -->
<include>包名1/类名1</include>
<include>包名2/类名2</include>
...
<!-- 可以是类名.class -->
<include>包名1/类名1.class</include>
<include>包名2/类名2.class</include>
正则匹配执行多个测试类:
<include>包名/*Test</include>
<include>包名/*Test.java</include>
<include>包名/*Test.class</include>
配置不被执行的测试类:
<exclude>包名/*Test.class</exclude>
<exclude>*Suite*Test</exclude>