SpringBoot实现单元测试示例详解
作者:Decade0712
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。这篇文章主要为大家介绍了C语言实现单元测试的方法,需要的可以参考一下
一、常用注解
官方文档:Junit5官网指导
- @Test :表示此方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一,不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
- @ParameterizedTest:参数化测试使用注解
- @RepeatedTest :表示测试方法可重复执行,value表示重复执行次数
- @DisplayName :为测试类或者测试方法设置展示名称
- @BeforeEach :表示在每个单元测试之前执行该方法
- @AfterEach :表示在每个单元测试之后执行该方法
- @BeforeAll :表示在所有开始单元测试之前执行,此方法必须是静态方法
- @AfterAll :表示在所有单元测试完成之后执行,此方法必须是静态方法
- @Tag :表示单元测试类别,类似于JUnit4中的@Categories
- @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
- @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
- @SpringBootTest:如果测试类想要使用Spring Boot的自动注入功能,例如@Autowired注解等,就需要在测试类上加上此注解
- @ExtendWith :为测试类或测试方法提供扩展类引用,类似于@RunWith,@RunWith(JUnit4.class) 就是指用JUnit4来运行,@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境
package com.decade; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import java.util.concurrent.TimeUnit; @DisplayName("测试类的名称为MyTest") @SpringBootTest public class MyTest { @Autowired private JdbcTemplate jdbcTemplate; @DisplayName("测试方法名称为test") @RepeatedTest(value = 3) public void test() { System.out.println("测试方法,打印JdbcTemplate类" + jdbcTemplate); } @Test @Timeout(value = 500, unit = TimeUnit.MILLISECONDS) public void testTimeOut() throws InterruptedException { Thread.sleep(600); } @BeforeEach public void beforeEach() { System.out.println("每个方法前执行"); } @AfterEach public void afterEach() { System.out.println("每个方法后执行"); } @BeforeAll static void beforeAll() { System.out.println("所有方法前执行"); } @AfterAll static void afterAll() { System.out.println("所有方法后执行"); } @Disabled @Test public void disableTest() { System.out.println("此内容不输出"); } }
二、断言机制
断言是测试方法中的核心部分,它用于检查业务逻辑返回的数据是否合理,不满足的断言会使得测试方法失败
如果是多个断言依次执行,只要前面的断言不通过,后面的就不会再执行了
1、简单断言
- assertEquals:判断两个对象或两个原始类型是否相等
- assertNotEquals:判断两个对象或两个原始类型是否不相等
- assertSame:判断两个对象引用是否指向同一个对象
- assertNotSame:判断两个对象引用是否指向不同的对象
- assertTrue:判断给定的布尔值是否为 true
- assertFalse:判断给定的布尔值是否为 false
- assertNull:判断给定的对象引用是否为 null
- assertNotNull:判断给定的对象引用是否不为 null
2、数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等
3、组合断言
通过assertAll方法接受多个函数式接口的实例作为要验证的断言,只要有一个不通过,就算失败
4、异常断言
通过assertThrow方法断定某个代码会抛出指定异常
5、超时异常
通过assertTimeout断定某个代码的执行时间会超过限制时间
6、快速失败
通过fail方法直接使得测试失败
package com.decade; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.temporal.ChronoUnit; @DisplayName("测试类为TestAssert") public class TestAssert { @Test @DisplayName("测试普通断言") public void testSimpleAssert() { Assertions.assertEquals(6, 2+3, "期望值与实际值不符"); Assertions.assertTrue(1 > 2, "条件不成立"); Object a = new Object(); Object b = new Object(); Assertions.assertSame(a, b, "不是同一个对象"); } @Test @DisplayName("测试数组断言") public void testArray() { Assertions.assertArrayEquals(new int[]{1, 2}, new int[]{2,1}, "数组不相等"); } @Test @DisplayName("测试组合断言") public void testCombination() { Assertions.assertAll("断言组合1", () -> Assertions.assertTrue(1 < 2, "判断条件不成立"), () -> Assertions.assertEquals(2, 3, "期望值与实际值不相符")); } @Test @DisplayName("测试异常断言") public void testException() { Assertions.assertThrows(ArithmeticException.class, () -> { System.out.println(1/0); }, "并没有抛出算数异常"); } @Test @DisplayName("测试超时断言") public void testTimeOut() { Assertions.assertTimeout(Duration.of(100, ChronoUnit.MILLIS), () -> Thread.sleep(500), "超时"); } @Test @DisplayName("测试快速失败fail") public void shouldFail() { Assertions.fail("This should fail"); } }
三、前置条件
前置和断言的不同之处在于,不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止
前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要
package com.decade; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @DisplayName("测试类为Test") public class Test { @Test @DisplayName("测试前置条件") public void testAssumptions() { Assumptions.assumeTrue(1 > 2, "条件不成立,后续步骤不再执行"); System.out.println("前置条件成立,继续执行到此步骤"); } }
四、嵌套测试
可以理解成测试套娃,需要注意的是,外层的测试方法无法驱动内层的测试方法,去执行内层测试方法的beforeEach、beforeAll、afterEach、afterAll
package com.decade; import org.junit.jupiter.api.*; import java.util.EmptyStackException; import java.util.Stack; @DisplayName("进行嵌套测试") public class TestAStackDemo { Stack<Object> stack; @Test @DisplayName("is instantiated with new Stack()") void isInstantiatedWithNew() { new Stack<>(); // 外层的test方法无法驱动内层的test,也就无法驱动下方的BeforeEach去实例化stack,所以这里会报空 Assertions.assertNull(stack); } @Nested @DisplayName("when new") class WhenNew { @BeforeEach void createNewStack() { stack = new Stack<>(); } @Test @DisplayName("is empty") void isEmpty() { Assertions.assertTrue(stack.isEmpty()); } @Test @DisplayName("throws EmptyStackException when popped") void throwsExceptionWhenPopped() { Assertions.assertThrows(EmptyStackException.class, stack::pop); } @Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { Assertions. assertThrows(EmptyStackException.class, stack::peek); } @Nested @DisplayName("after pushing an element") class AfterPushing { String anElement = "an element"; // 内层的test可以驱动外层的test,这里会驱动上方的beforeEach去实例化stack @BeforeEach void pushAnElement() { stack.push(anElement); } @Test @DisplayName("it is no longer empty") void isNotEmpty() { Assertions.assertFalse(stack.isEmpty()); } @Test @DisplayName("returns the element when popped and is empty") void returnElementWhenPopped() { Assertions.assertEquals(anElement, stack.pop()); Assertions.assertTrue(stack.isEmpty()); } @Test @DisplayName("returns the element when peeked but remains not empty") void returnElementWhenPeeked() { Assertions.assertEquals(anElement, stack.peek()); Assertions.assertFalse(stack.isEmpty()); } } } }
五、参数化测试
- @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
- @NullSource: 表示为参数化测试提供一个null的入参
- @EnumSource: 表示为参数化测试提供一个枚举入参
- @CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
- @MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
package com.decade; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.util.stream.Stream; @DisplayName("进行参数化测试") public class Test { @ParameterizedTest @ValueSource(strings = {"decade", "hhh", "LOL"}) @DisplayName("参数化测试,传入String类型依次测试") public void testParameter(String variables) { System.out.println(variables); } @ParameterizedTest // 此处为方法名,方法必须返回stream流,并且是静态的 @MethodSource("creatStream") @DisplayName("参数化测试,传入String类型依次测试") public void testParameterIsMethod(String variables) { System.out.println(variables); } static Stream<String> creatStream() { return Stream.of("decade", "hhh", "basketball"); } }
测试结果如图
到此这篇关于SpringBoot实现单元测试示例详解的文章就介绍到这了,更多相关SpringBoot单元测试内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!