赞
踩
SpringBoot 2.2.0 版本开始引入 JUnit5 作为单元测试默认库
作为新版本的 JUnit 框架,JUnit5 与之前的版本的 JUnit 框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
注意:SpringBoot 2.4 以上版本默认移除了 JUnit Vintage 的依赖。如果需要兼容 JUnit4 需要自行引入。如果要继续兼容 JUnit4 需要自行引入 JUnit Vintage。
- <dependency>
- <groupId>org.junit.vintage</groupId>
- <artifactId>junit-vintage-engine</artifactId>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-core</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
单元测试的依赖:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
现在版本使用方式:
- @SpringBootTest
- class Boot05Web01ApplicationTests {
-
- @Test
- void contextLoads() {
- }
- }
之前版本使用方式:
- @SpringBootTest
- @RunWith(SpringTest.class)
- class XxxTests {
- }
SpringBoot 整合 Junit 后
- @SpringBootTest //@SpringBootTest 就是一个复合注解,它内部包含 @ExtendWith({SpringExtension.class})。加上此注解表示支持 SpringBoot 功能
- @DisplayName("测试 Junit5")
- public class Junit5Test {
- @DisplayName("测试 testDisplayName")
- @Test
- void testDisplayName() {
- System.out.println(1);
- }
-
- @Test
- void test2() {
- System.out.println(2);
- }
-
- @RepeatedTest(value = 4)
- @Test
- void testRepeatedTest(){
- System.out.println(3);
- }
-
- @Disabled
- @Test
- void testDisabled() {
- System.out.println(5);
- }
-
- @Timeout(1) //执行时间超过1秒异常 java.util.concurrent.TimeoutException
- @Test
- void testTimeout() throws InterruptedException {
- Thread.sleep(1100);
- }
-
- /**
- * @ValueSource 注解为每次执行传入的参数
- * 本次输出的结果:
- * 每次开始前执行
- * 11
- * 每次结束执行
- *
- * 每次开始前执行
- * 22
- * 每次结束执行
- *
- * 每次开始前执行
- * 33
- * 每次结束执行
- */
- @ParameterizedTest
- @ValueSource(strings={"11","22","33","44","55"})
- void testParameterizedTest(String param) {
- System.out.println(param);
- }
-
- @BeforeEach
- void testBeforeEach() {
- System.out.println("每次开始前执行");
- }
-
- @AfterEach
- void testAfterEach() {
- System.out.println("每次结束执行");
- }
-
- //@BeforeAll 标注的方法必须是 static
- @BeforeAll
- static void testBeforeAll(){
- System.out.println("全部开始前执行");
- }
-
- //@AfterAll 标注的方法必须是 static
- @AfterAll
- static void testAfterAll() {
- System.out.println("全部结束后执行");
- }
- }
断言 Assertion 是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言的方法都是 org.junit.jupiter.api.Assertions 的静态方法。检查业务逻辑返回的数据是否合理。所有的测试运行结束以后,会有一个详细的测试报告。所有推荐使用断言机制。
用来验证对单个值进行简单的验证。如:
方法 | 说明 |
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
- /**
- * 断言:前面断言失败,后面的代码不会继续执行
- */
- @Test
- @DisplayName("测试简单断言")
- void testSimpleAssertions() {
- int v = add(2,3);
- //判定相等
- assertEquals(5,v);
- // assertEquals(6,v,"不相等");
- assertNotEquals(6,v);
- Object obj1 = new Object();
- Object obj2 = new Object();
- // assertSame(obj1,obj2,"两个对象不一样");
- assertNotSame(obj1,obj2);
- assertTrue(true);
- assertFalse(false);
- assertNull(null);
- assertNotNull(obj1);
- }
-
- int add(int i, int j){
- return i+j;
- }
- @Test
- @DisplayName("测试数组断言")
- void testArray() {
- assertArrayEquals(new int[]{1,2}, new int[]{1,2});
- }
- /**
- * 只有所有断言成功,才能往下走
- */
- @Test
- @DisplayName("测试组合断言")
- void testAll() {
- assertAll(
- ()->assertTrue(true),
- ()->assertEquals(1,2)
- );
- }
- @Test
- @DisplayName("测试异常断言")
- void testException() {
- //断定业务逻辑一定出现异常
- assertThrows(ArithmeticException.class, ()->{
- int i=10/0;
- },"业务逻辑居然正常");
- }
- @Timeout(1) //执行时间超过1秒异常 java.util.concurrent.TimeoutException
- @Test
- void testTimeout() throws InterruptedException {
- Thread.sleep(1100);
- }
- @Test
- @DisplayName("测试快速失败")
- void testFail() {
- fail("测试失败");
- }
Junit5 中的前置条件(assumptions 【假设】)类似于断言,不同支出在于不满足的断言 assertions 会使得测试方法失败,而不满足前置条件只会使得测试方法执行终止。
- @DisplayName("测试前置条件")
- @Test
- void testAssumptions() {
- Assumptions.assumeTrue(false,"结果不是 true");
-
- }
它和 @Disable 的效果类似,如下图。
Junit5 可以通过 Java 中的内部类和 @Nested 注解实现嵌套测试,从而可以更好地把相关的测试方法组织在一起。在内部类中可以使用 @BeforeEach 和 @AfterEach 注解,而且嵌套层次没有限制。
嵌套测试情况下:
- @DisplayName("A stack")
- class TestingAStackDemo {
-
- Stack<Object> stack;
-
- @Test
- @DisplayName("is instantiated with new Stack()")
- void isInstantiatedWithNew() {
- new Stack<>();
- //嵌套测试情况下,外侧的Test不能驱动内层类的Before(After)Each/All 之类的方法。
- assertNotNull(stack);
- }
-
- @Nested
- @DisplayName("when new")
- class WhenNew {
-
- @BeforeEach
- void createNewStack() {
- stack = new Stack<>();
- }
-
- @Test
- @DisplayName("is empty")
- void isEmpty() {
- assertTrue(stack.isEmpty());
- }
-
- @Test
- @DisplayName("throws EmptyStackException when popped")
- void throwsExceptionWhenPopped() {
- assertThrows(EmptyStackException.class, stack::pop);
- }
-
- @Test
- @DisplayName("throws EmptyStackException when peeked")
- void throwsExceptionWhenPeeked() {
- assertThrows(EmptyStackException.class, stack::peek);
- }
-
- @Nested
- @DisplayName("after pushing an element")
- class AfterPushing {
-
- String anElement = "an element";
-
- @BeforeEach
- void pushAnElement() {
- stack.push(anElement);
- }
-
- //内层的Test可以驱动外侧的 Before(After)Each/All 之类的方法
- @Test
- @DisplayName("it is no longer empty")
- void isNotEmpty() {
- assertFalse(stack.isEmpty());
- }
-
- @Test
- @DisplayName("returns the element when popped and is empty")
- void returnElementWhenPopped() {
- assertEquals(anElement, stack.pop());
- assertTrue(stack.isEmpty());
- }
-
- @Test
- @DisplayName("returns the element when peeked but remains not empty")
- void returnElementWhenPeeked() {
- assertEquals(anElement, stack.peek());
- assertFalse(stack.isEmpty());
- }
- }
- }
- }
参数化测试是 Junit5 很重要的一个特性,它使得不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用 @ValueSource 等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我们觉得经验的地步。它真正强大之处在于它可以支持外部的各类入参。如:CSV、YML、JSON 文件 甚至方法的返回值也可以作为入参。只需要去实现【ArgumentsProvider】接口,任何外部文件都可以作为它的入参。
- @DisplayName("参数化测试")
- public class TestParamTest {
-
- @DisplayName("测试 ValueSource")
- @ParameterizedTest
- @ValueSource(ints = {1,2,3,4,5})
- void testParameterized(int i) {
- System.out.println(i);
- }
-
- @DisplayName("测试 ValueSource")
- @ParameterizedTest
- @MethodSource("method") //指定方法名称
- void testParameterized2(String str) {
- System.out.println(str);
- }
-
- static Stream<String> method() {
- return Stream.of("apple", "banana");
- }
- }
在迁移的时候注意如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。