🤖🤖-摘要: 本文介绍了SpringBoot单元测试的方法和使用。通过JUnit和MockMvc框架,可以精确控制测试粒度,提高单元测试的效率和质量。文章详述了Springboot单元测试相关注解,断言,嵌套测试以及参数化测试的使用方法,帮助开发者系统性的理解和掌握SpringBoot单元测试相关知识。
概述 在平时的开发当中,一个项目往往包含了大量的方法,可能有成千上万个。如何去保证这些方法产生的结果是我们想要的呢?
传统解决方案:Postman 发报文,System.out打印debug日志,或者眼睛看返回报文
眼睛看结果是否正确,瞅瞎不说,也太不智能.我们是高智商程序员,能让代码解决的事情,绝不能靠人工去解决 postman 只能对controller进行测试。controller要正确,前提是service,dao都正确。发现问题太晚,解决成本高 对于一些交易系统,由于交易主键的存在,每次都要更改参数后,再进行测试,效率太低 无法对内部的函数功能做测试 postman的测试案例与项目工程不再一起,这些案例只能自己一个人用,无法团队共享 这时,就轮到单元测试闪亮出场了
测试代码和工程代码在同一工程文件中,便于维护和传承 使用断言自动检测结果 测试粒度小,可以小到每个函数 测试模块间相互依赖小。开发完一个模块,就可以测试一个模块。妈妈再也不用担心我犯大错了 SpringBoot单元测试 业界单元测试一般采用基于JUnit 和MockMvc 框架进行
JUnit : 是通用测试框架,主要进行Dao层和Service层测试MockMvc : 主要进行Controller层测试相关注解 JUnit5 的注解与JUnit4 的注解有所变化,详见官方文档
@Test:表示方法是测试方法。但是与JUnit4 的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试@ParameterizedTest: 表示方法是参数化测试@RepeatedTest: 表示方法可重复执行@DisplayName: 为测试类或者测试方法设置展示名称@BeforeEach: 表示在每个单元测试之前执行@AfterEach: 表示在每个单元测试之后执行@BeforeAll: 表示在所有单元测试之前执行@AfterAll: 表示在所有单元测试之后执行@Tag: 表示单元测试类别,类似于JUnit4 中的@Categories@Disabled: 表示测试类或测试方法不执行,类似于JUnit4 中的@Ignore@Timeout: 表示测试方法运行如果超过了指定时间将会返回错误@ExtendWith: 为测试类或测试方法提供扩展类引用class StandardTests { @BeforeAll static void initAll () { System.out.println("开始单元测试:" ); } @BeforeEach void init () { System.out.println("方法执行前..." ); } @DisplayName("测试名称") @Test void succeedingTest () { } @Test void failingTest () { fail("a failing test" ); } @Test @Disabled("for demonstration purposes") void skippedTest () { } @Test void abortedTest () { assumeTrue("abc" .contains("Z" )); fail("test should have been aborted" ); } @AfterEach void tearDown () { System.out.println("方法执行结束..." ); } @AfterAll static void tearDownAll () { System.out.println("单元测试结束" ); } }
断言 方法 说明 assertEquals 判断两个对象或两个原始类型是否相等 assertNotEquals 判断两个对象或两个原始类型是否不相等 assertSame 判断两个对象引用是否指向同一个对象 assertNotSame 判断两个对象引用是否指向不同的对象 assertTrue 判断给定的布尔值是否为 true assertFalse 判断给定的布尔值是否为 false assertNull 判断给定的对象引用是否为 null assertNotNull 判断给定的对象引用是否不为 null assertArrayEquals 数组断言 assertAll 组合断言 assertThrows 异常断言 assertTimeout 超时断言 fail 快速失败
嵌套测试 JUnit5 可以通过Java中的内部类和@Nested注解实现嵌套测试, 从而可以更好的把相关的测试方法组织在一起. 在内部类中可以使用@BeforeEach和@AfterEach注解, 而且嵌套的层次没有限制
@DisplayName("A stack") class TestingAStackDemo { Stack<Object> stack; @Test @DisplayName("is instantiated with new Stack()") void isInstantiatedWithNew () { new 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 @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等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型@NullSource: 表示为参数化测试提供一个null的入参@EnumSource: 表示为参数化测试提供一个枚举入参@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)@ParameterizedTest @ValueSource(strings = {"one", "two", "three"}) @DisplayName("参数化测试1") public void parameterizedTest1 (String string) { System.out.println(string); Assertions.assertTrue(StringUtils.isNotBlank(string)); } @ParameterizedTest @MethodSource("method") @DisplayName("方法来源参数") public void testWithExplicitLocalMethodSource (String name) { System.out.println(name); Assertions.assertNotNull(name); } static Stream<String> method () { return Stream.of("apple" , "banana" ); }
版权声明: 此文章版权归作者所有,如有转载,请注明来自原作者