本文共 9977 字,大约阅读时间需要 33 分钟。
内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
《JUnit5学习》系列旨在通过实战提升SpringBoot环境下的单元测试技能,一共八篇文章,链接如下:
名称 | 链接 | 备注 |
---|---|---|
项目主页 | 该项目在GitHub上的主页 | |
git仓库地址(https) | 该项目源码的仓库地址,https协议 | |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
<dependencyManagement> <dependencies> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.7.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> </exclusion> </exclusions></dependency>
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope></dependency>
3. 除了用@DisplayName指定展示名称,JUnit5还提供了一种自动生成展示名称的功能:@DisplayNameGeneration,来看看它是如何生成展示名称的;
4. 演示代码如下所示,当@DisplayNameGeneration的value设置为ReplaceUnderscores时,会把方法名的所有下划线替换为空格:
package com.bolingcavalry.advanced.service.impl;import org.junit.jupiter.api.DisplayNameGeneration;import org.junit.jupiter.api.DisplayNameGenerator;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)public class ReplaceUnderscoresTest { @Test void if_it_is_zero() { }}
6. 在上述替换方式的基础上,JUnit5还提供了另一种生成展示名称的方法:测试类名+连接符+测试方法名,并且类名和方法名的下划线都会被替换成空格,演示代码如下,使用了注解@IndicativeSentencesGeneration,其separator属性就是类名和方法名之间的连接符:
package com.bolingcavalry.advanced.service.impl;import org.junit.jupiter.api.DisplayNameGenerator;import org.junit.jupiter.api.IndicativeSentencesGeneration;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest@IndicativeSentencesGeneration(separator = ",测试方法:", generator = DisplayNameGenerator.ReplaceUnderscores.class)public class IndicativeSentences_Test { @Test void if_it_is_one_of_the_following_years() { }}
@Order(1) @DisplayName("重复测试") @RepeatedTest(5) void repeatTest(TestInfo testInfo) { log.info("测试方法 [{}]", testInfo.getTestMethod().get().getName()); }
3. 在测试方法执行时,如果想了解当前是第几次执行,以及总共有多少次,只要给测试方法增加RepetitionInfo类型的入参即可,演示代码如下,可见RepetitionInfo提供的API可以得到总数和当前次数:
@Order(2) @DisplayName("重复测试,从入参获取执行情况") @RepeatedTest(5) void repeatWithParamTest(TestInfo testInfo, RepetitionInfo repetitionInfo) { log.info("测试方法 [{}],当前第[{}]次,共[{}]次", testInfo.getTestMethod().get().getName(), repetitionInfo.getCurrentRepetition(), repetitionInfo.getTotalRepetitions()); }
5. 在上图的左下角可见,重复执行的结果被展示为"repetition X of X"这样的内容,其实这部分信息是可以定制的,就是RepeatedTest注解的name属性,演示代码如下,可见currentRepetition和totalRepetitions是占位符,在真正展示的时候会被分别替换成当前值和总次数:
@Order(3) @DisplayName("重复测试,使用定制名称") @RepeatedTest(value = 5, name="完成度:{currentRepetition}/{totalRepetitions}") void repeatWithCustomDisplayNameTest(TestInfo testInfo, RepetitionInfo repetitionInfo) { log.info("测试方法 [{}],当前第[{}]次,共[{}]次", testInfo.getTestMethod().get().getName(), repetitionInfo.getCurrentRepetition(), repetitionInfo.getTotalRepetitions()); }
3. 嵌套测试的演示代码如下:
package com.bolingcavalry.advanced.service.impl;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.DisplayName;import org.junit.jupiter.api.Nested;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest@Slf4j@DisplayName("嵌套测试演示")public class NestedTest { @Nested @DisplayName("查找服务相关的测试") class FindService { @Test void findByIdTest() {} @Test void findByNameTest() {} } @Nested @DisplayName("删除服务相关的测试") class DeleteService { @Test void deleteByIdTest() {} @Test void deleteByNameTest() {} }}
package com.bolingcavalry.advanced.service.impl;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.DynamicTest;import org.junit.jupiter.api.TestFactory;import org.springframework.boot.test.context.SpringBootTest;import java.util.Arrays;import static org.junit.jupiter.api.DynamicTest.dynamicTest;@SpringBootTest@Slf4jclass DynamicDemoTest { @TestFactory Iterable<org.junit.jupiter.api.DynamicTest> testFactoryTest() { DynamicTest firstTest = dynamicTest( "一号动态测试用例", () -> { log.info("一号用例,这里编写单元测试逻辑代码"); } ); DynamicTest secondTest = dynamicTest( "二号动态测试用例", () -> { log.info("二号用例,这里编写单元测试逻辑代码"); } ); return Arrays.asList(firstTest, secondTest); }}
# 并行开关true/falsejunit.jupiter.execution.parallel.enabled=true# 方法级多线程开关 same_thread/concurrentjunit.jupiter.execution.parallel.mode.default = same_thread# 类级多线程开关 same_thread/concurrentjunit.jupiter.execution.parallel.mode.classes.default = same_thread# 并发策略有以下三种可选:# fixed:固定线程数,此时还要通过junit.jupiter.execution.parallel.config.fixed.parallelism指定线程数# dynamic:表示根据处理器和核数计算线程数# custom:自定义并发策略,通过这个配置来指定:junit.jupiter.execution.parallel.config.custom.classjunit.jupiter.execution.parallel.config.strategy = fixed# 并发线程数,该配置项只有当并发策略为fixed的时候才有用junit.jupiter.execution.parallel.config.fixed.parallelism = 5
package com.bolingcavalry.advanced.service.impl;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.*;import org.junit.jupiter.api.parallel.Execution;import org.junit.jupiter.api.parallel.ExecutionMode;import org.junit.jupiter.params.ParameterizedTest;import org.junit.jupiter.params.provider.ValueSource;import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.assertTrue;@SpringBootTest@Slf4j@TestMethodOrder(MethodOrderer.OrderAnnotation.class)class ParallelExecutionTest { @Order(1) @Execution(ExecutionMode.SAME_THREAD) @DisplayName("单线程执行10次") @RepeatedTest(value = 10, name="完成度:{currentRepetition}/{totalRepetitions}") void sameThreadTest(TestInfo testInfo, RepetitionInfo repetitionInfo) { log.info("测试方法 [{}],当前第[{}]次,共[{}]次", testInfo.getTestMethod().get().getName(), repetitionInfo.getCurrentRepetition(), repetitionInfo.getTotalRepetitions()); }}
@Order(2) @Execution(ExecutionMode.CONCURRENT) @DisplayName("多线程执行10次") @RepeatedTest(value = 10, name="完成度:{currentRepetition}/{totalRepetitions}") void concurrentTest(TestInfo testInfo, RepetitionInfo repetitionInfo) { log.info("测试方法 [{}],当前第[{}]次,共[{}]次", testInfo.getTestMethod().get().getName(), repetitionInfo.getCurrentRepetition(), repetitionInfo.getTotalRepetitions()); }
11. 最后是参数化测试的演示,也可以设置为多线程并行执行:
@Order(3) @Execution(ExecutionMode.CONCURRENT) @DisplayName("多个int型入参") @ParameterizedTest @ValueSource(ints = { 1,2,3,4,5,6,7,8,9,0 }) void intsTest(int candidate) { log.info("ints [{}]", candidate); }
至此,《JUnit5学习》系列已经全部完成,感谢您的耐心阅读,希望这个原创系列能够带给您一些有用的信息,为您的单元测试提供一些参考,如果发现文章有错误,期待您能指点一二;
微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...
转载地址:http://yhtkz.baihongyu.com/