Java单元测试----Junit详解
1 什么是 Junit
JUnit 是一个广泛使用的 Java 单元测试框架。它用于编写和运行可重复的测试,以验证 Java 程序的行为是否符合预期
也许有人会好奇,之前学的 Selenium 和 Junit 有什么关系?答案就是没关系!
这里,我们使用的是 Junit5
2 前期准备
在Java中,对于一个普通的maven项目,我们要使用 Junit 是需要先导入相应的依赖的。(SpringBoot 项目集成了,不需要额外导入依赖)。
下面是需要导入的依赖:
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.9.1</version></dependency><!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-params</artifactId><version>5.9.1</version></dependency><!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite --><dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-suite</artifactId><version>1.9.1</version></dependency><!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine</artifactId><version>5.9.1</version><scope>test</scope></dependency>
下面是这些依赖的作用:如果不理解的话,可以先看下面的核心功能,有了这些依赖,才能使用下面的功能
3 核心功能
3.1 常用的注解
注解:用于标识和管理测试方法和生命周期方法。
@Test
:标识一个测试方法。@BeforeEach
和@AfterEach
:在每个测试方法之前和之后运行的方法。@BeforeAll
和@AfterAll
:在所有测试方法之前和之后运行的方法,通常用于静态方法。
下面分别来看一下这些注解:
@Test
@Test 用来表示当前的方法是一个测试方法,在 Junit 中,我们可以单独执行这个方法
我们只运行第一个,就会得到下面的效果:
如果点击类名旁边的那个绿三角,则会都运行:
说明:
@Test
注解的方法不需要是public
的,但必须是无参数的
@BeforeEach @AfterEach
@BeforeEach
- 功能:在每个测试方法之前运行的方法。
- 使用场景:用于在每个测试方法执行之前设置测试环境,如初始化变量或对象
@AfterEach
- 功能:在每个测试方法之后运行的方法。
- 使用场景:用于在每个测试方法执行之后清理测试环境,如释放资源或重置变量。
下面看一个简答的例子:
运行效果:
@BeforeAll @AfterAll
@BeforeAll
- 功能:在所有测试方法之前运行的方法,通常只运行一次。
- 使用场景:用于执行一次性的全局初始化,如设置静态资源或配置共享的状态。
@AfterAll
- 功能:在所有测试方法之后运行的方法,通常只运行一次。
- 使用场景:用于执行一次性的全局清理,如关闭静态资源或释放共享的状态。
说明:
- 其修饰方法必须是
static
的。(这篇文章中 点击跳转 讲解了为什么必须被static
修饰)- 只会运行一次,无论有多少个测试方法。
简单的例子:在刚才代码的基础上加上这两句
运行效果:
3.2 测试用例的执行顺序
测试用例的执行顺序是什么呢?会不会有人刚开始跟我一样,认为是按照顺序执行的呢?下面咱们来看一下:
@Test
void test04() {System.out.println("这是第四个测试方法");
}@Test
void test01() {System.out.println("这是第一个测试方法");
}@Test
void test03() {System.out.println("这是第三个测试方法");
}@Test
void test02() {System.out.println("这是第二个测试方法");
}
这是四个测试方法,如果按照顺序执行,那么应该是: 4132,下面来看一下,是不是这样呢?
结果发现,根本不是我们想的那样,怎么才能让他按照我们设置的顺序来直接呢?我们可以 指定顺序
指定顺序
JUnit 5 引入了 @TestMethodOrder
注解,用于指定测试方法的执行顺序。可以通过以下几种方式来控制顺序:
- OrderAnnotation:按
@Order
注解的值来执行。 - MethodName:按方法名的字典顺序来执行。
- Alphanumeric:按字母和数字顺序来执行。
- Custom:自定义顺序,通过实现
MethodOrderer
接口来定义
在日常使用中,我们多用第一个,下面对第一个举例:
需要现在类前加入注解:@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
然后在每个测试方法前加上 @Order(数字)
,最后的执行顺序就按照 @Order
注解的值来执行
这次,执行顺序应该就和我们刚刚想的一样,是 4132
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JunitTest3 {@Order(1)@Testvoid test04() {System.out.println("这是第4个测试方法");}@Order(2)@Testvoid test01() {System.out.println("这是第1个测试方法");}@Order(3)@Testvoid test03() {System.out.println("这是第3个测试方法");}@Order(4)@Testvoid test02() {System.out.println("这是第2个测试方法");}
}
3.3 参数化
参数化就是测试方法里有参数,上面写到,使用 @Test
时,方法必须是无参的,所以,使用参数化的方法时,就不能使用 @Test
了,不然会报错。
单参数
使用 @ParameterizedTest
和 @ValueSource()
@ParameterizedTest
标识一个参数化测试方法
@ValueSource()
是用来传参的,支持的值类型包括:short、byte、int、long、float、double、char、boolean 和 String。
示例
以下是一个使用 @ParameterizedTest
和 @ValueSource
的简单示例:
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4}) // @ValueSource(ints = 1)
void Test03(int x) {System.out.println("接收的参数是x = " + x);
}
@ParameterizedTest
:标识Test03
方法是一个参数化测试。
@ValueSource(ints = {1, 2, 3, 4})
:提供一组整型参数。这个测试方法将使用这些参数分别运行四次,每次传入一个数值。也可以只传入一个
@ValueSource(ints = 1)
,这样就只会输出:接收的参数是x = 1
@ValueSource
的其他类型
@ValueSource
可以用来提供不同类型的简单值。以下是一些例子:
-
整数类型:
@ParameterizedTest @ValueSource(ints = {1, 2, 3, 4, 5}) void testWithIntValues(int number) {...... }
-
字符类型:
@ParameterizedTest @ValueSource(chars = {'A', 'B', 'C'}) void testWithCharValues(char ch) {...... }
-
布尔类型:
@ParameterizedTest @ValueSource(booleans = {true, false}) void testWithBooleanValues(boolean value) {...... }
也就是基本类型加个s,
shorts
:用于短整型数组 (short
)
bytes
:用于字节数组 (byte
)
ints
:用于整型数组 (int
)
longs
:用于长整型数组 (long
)
floats
:用于浮点型数组 (float
)
doubles
:用于双精度浮点型数组 (double
)
chars
:用于字符数组 (char
)
booleans
:用于布尔型数组 (boolean
)
strings
:用于字符串数组 (String
)
多参数
使用 @ParameterizedTest
和 @CsvSource()
例子:
单个参数
@ParameterizedTest
@CsvSource({"apple", "banana","cherry"})void test(String fruit) {System.out.println(fruit);
}
多个参数
@ParameterizedTest
@CsvSource({"apple, 1", "banana, 2", "cherry, 3"})void test(String fruit, int rank) {System.out.println(fruit + rank);
}
含有特殊字符和引号
当 CSV 中包含特殊字符或逗号时,可以使用引号将其括起来:
@ParameterizedTest
@CsvSource({"'apple, green', 1", "'banana, yellow', 2", "'cherry, red', 3"})void test(String fruit, int rank) {System.out.println(fruit + " " + rank);
}
使用文件传递参数
当参数较多的时候,还可以通过文件来传递参数
@CsvFileSource(resources = "文件名.csv")
// 注意:文件必须是.csv类型
例子:
@ParameterizedTest
@CsvFileSource(resources = "test01.csv")void Test05(String name, int age) {System.out.println("name:" + name + ", age:" + age);
}
test01.csv 文件的内容:使用逗号分隔
结果:
通过方法生成参数
@MethodSource("方法名")
@ParameterizedTest
@MethodSource("Generate")void Test06(String name, int age) {System.out.println("姓名:" + name + "年龄:" + age);
}// 这个方法用来创建参数
public static Stream<Arguments> Generate() {return Stream.of(Arguments.arguments("张三", 13),Arguments.arguments("李四",14));
}
3.4 测试套件
在 JUnit 中,测试套件(Test Suite)用于将多个测试类捆绑在一起,以便于一次性运行。
JUnit 5 中的测试套件
在 JUnit 5 中,测试套件通过 @Suite
、@SelectPackages
和 @SelectClasses
注解来定义。以下是创建一个测试套件的步骤和示例:
1. 创建测试类
首先,创建一些测试类(与上面相同):
public class TestClass1 {@Testvoid test1() {System.out.println("1");}...
}public class TestClass2 {@Testvoid test2() {System.out.println("2");}...
}
2. 创建测试套件类
然后,创建一个测试套件类,将这些测试类包含在一起:
通过class运行测试用例
@Suite
@SelectClasses({ TestClass1.class, TestClass2.class })
public class TestSuite {// 这个类保持为空,它的目的是作为持有注解的入口
}
这段代码将执行 TestClass1 和 TestClass1这两个类中的全部测试用例
按包选择测试类:
@Suite
@SelectPackages(value = {"example", "example2")
public class TestSuite {// 这个类保持为空,它的目的是作为持有注解的入口
}
在这个示例中,将执行 example 和 example2 这两个包中的全部测试用例
3.5 断言
在软件测试中,断言(assertion)是用于验证程序行为是否符合预期的重要工具。断言方法用于验证测试中期望的结果与实际结果是否一致,如果不一致,测试将失败并报告错误。这样,就不用我们自己写 if 等来判断了。
在 Junit 中 使用 Assertions 这个类来调用他的方法
一些常见的方法:
assertEquals(expected, actual)
:验证两个值是否相等。
assertNotEquals(unexpected, actual)
:验证两个值是否不相等。
assertTrue(condition)
:验证条件是否为真。
assertFalse(condition)
:验证条件是否为假。
assertNull(object)
:验证对象是否为空。
assertNotNull(object)
:验证对象不为空。
assertThrows(expectedType, executable)
:验证代码抛出了预期的异常。
assertTimeout(Duration, executable)
:验证代码在规定时间内执行完毕。
下面来看一个简单的例子:
@Test
void test07() {int x = 10;int y = 20;Assertions.assertEquals(x,y);String temp = null;Assertions.assertNull(temp);
}
如果出错,后面的也不会执行
还可以点击 Click to see difference 来查看预期结果和真实结果之间的差异