一、前言
- 最近测试一个开源项目,发现生成的
全局id有重复,也没有单元测试,就准备贡献个PR - 想到多线程并发测试,根据经验,第一想法是用 Testng,后面看了下 Junit5 也有实验性支持了,就对比下(以
maven为例) - spock 不太适合这种场景
二、Testng
1. 安装
- 选择 使用数 比较多、也比较新 的版本,7.7.1。<testng.version>7.7.1</testng.version>
- 多模块项目,可以在 根pom.xml里面添加依赖,避免每个模块都增加配置哦
1 <dependencies> 2 <!-- test --> 3 <dependency> 4 <groupId>org.junit.jupiter</groupId> 5 <artifactId>junit-jupiter</artifactId> 6 <scope>test</scope> 7 </dependency> 8 </dependencies>
2. 使用
- @Test 就可以指定 执行次数(invocationCount)、并发(threadPoolSize),很方便
- 同一个测试对象,ids属性 不用加 static
1public class UniqueIdGeneratorTest { 2 private Set<Long> ids = new ConcurrentHashSet<>(128); 3 4 @org.testng.annotations.Test(invocationCount = 128, threadPoolSize = 3) 5 public void testGenerateId() { 6 final Long id = UniqueIdGenerator.generateId(); 7 assertTrue(ids.add(id), "id exists," + id); 8 } 9 10}
3. 效果

三、Junit5
1. 安装
- 选择 使用数 比较多、也比较新 的版本,5.8.2。<junit-jupiter.version>5.8.2</junit-jupiter.version>
- 最好通过 dependencyManagement 来统一版本,尤其是 多模块项目
- 建议放到 spring-boot-dependencies 前面,优先级更高
1 <dependencyManagement> 2 <dependencies> 3 <dependency> 4 <groupId>org.junit</groupId> 5 <artifactId>junit-bom</artifactId> 6 <version>${junit-jupiter.version}</version> 7 <type>pom</type> 8 <scope>import</scope> 9 </dependency> 10 </dependencies> </dependencyManagement>
多模块项目,可以在 根pom.xml里面添加依赖,避免每个模块都增加配置哦
1 <dependencies> 2 <!-- test --> 3 <dependency> 4 <groupId>org.testng</groupId> 5 <artifactId>testng</artifactId> 6 <version>${testng.version}</version> 7 <scope>test</scope> 8 </dependency> 9 </dependencies>
2 配置项
-
先设置 junit.jupiter.execution.parallel.enabled=true
-
全局并发测试:junit.jupiter.execution.parallel.mode.default=concurrent
-
局部并发测试:@RepeatedTest 和 @Execution(CONCURRENT)
-
按需配置并行策略 strategy,建议 fixed,线程数 可控
-
fixed 配置:如 fixed.parallelism=3,fixed.max-pool-size=3(默认 256 + parallelism)

-
dynamic策略(根据 factor、CPU核数 调整parallelism),parallelism = max(1, factor * CPU核数),后面和 fixed 逻辑一样

3 配置方式
- System properties 配置方式,更适合多模块项目(根pom.xml配置,子模块就不用配置了)
1 <plugin> 2 <groupId>org.apache.maven.plugins</groupId> 3 <artifactId>maven-surefire-plugin</artifactId> 4 <version>2.18.1</version> 5 <configuration> 6 <argLine>-Djunit.jupiter.execution.parallel.enabled=true -Djunit.jupiter.execution.parallel.config.strategy=fixed 7 -Djunit.jupiter.execution.parallel.config.fixed.parallelism=3 -Djunit.jupiter.execution.parallel.config.fixed.max-pool-size=3 8 </argLine> 9 </configuration> 10 </plugin>
- 配置文件 test/resources 目录下,增加 junit-platform.properties 文件,内容如下:
1#是否允许并行执行true/false 2junit.jupiter.execution.parallel.enabled=true 3#是否支持方法级别多线程same_thread/concurrent 4junit.jupiter.execution.parallel.mode.default=concurrent 5#是否支持类级别多线程same_thread/concurrent 6junit.jupiter.execution.parallel.mode.classes.default=concurrent 7# the maximum pool size can be configured using a ParallelExecutionConfigurationStrategy 8junit.jupiter.execution.parallel.config.strategy=fixed 9junit.jupiter.execution.parallel.config.fixed.parallelism=3 10junit.jupiter.execution.parallel.config.fixed.max-pool-size=3
4. 使用
- 需改为 @RepeatedTest,和 普通测试不一致,并发线程数 是全局的
- 测试对象 不同,ids属性 需加 static
1class UniqueIdGeneratorTest2 { 2 private static Set<Long> ids = new ConcurrentHashSet<>(128); 3 4 @org.junit.jupiter.api.RepeatedTest(128) 5 @org.junit.jupiter.api.parallel.Execution(ExecutionMode.CONCURRENT) 6 void generateId() { 7 final Long id = UniqueIdGenerator.generateId(); 8 assertTrue(ids.add(id), "id exists," + id); 9 } 10}
5. 效果

四、总结
- 多线程并发测试 场景,Testng 可能比 Junit5 更合适
- 使用不多也可以试试 hutool 的 ThreadUtil#concurrencyTest(int threadSize, Runnable runnable)
本文遵守【CC BY-NC】协议,转载请保留原文出处及本版权声明,否则将追究法律责任。
本文首先发布于 https://www.890808.xyz/ ,其他平台需要审核更新慢一些。

