拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 JUnit 5如何执行并行测试

JUnit 5如何执行并行测试

白鹭 - 2021-11-06 2245 0 2

一、简介

在本文中,我们将介绍如何使用JUnit 5 执行并行单元测试。首先,我们将介绍开始使用此功能的基本配置和最低要求。接下来,我们将展示针对不同情况的代码示例,最后我们将讨论共享资源的同步。

并行测试执行是一项实验性功能,自5.3 版起可选择加入。

2. 配置

首先,我们通过在提到的文件中添加以下行来启用并行化功能:src/test/resources junit-platform.properties

junit.jupiter.execution.parallel.enabled = true

让我们通过运行一些测试来检查我们的配置。首先,我们将创建FirstParallelUnitTest

public class FirstParallelUnitTest{
 @Test
 public void first() throws Exception{
 System.out.println("FirstParallelUnitTest first() start => " + Thread.currentThread().getName());
 Thread.sleep(500);
 System.out.println("FirstParallelUnitTest first() end => " + Thread.currentThread().getName());
 }
 @Test
 public void second() throws Exception{
 System.out.println("FirstParallelUnitTest second() start => " + Thread.currentThread().getName());
 Thread.sleep(500);
 System.out.println("FirstParallelUnitTest second() end => " + Thread.currentThread().getName());
 }
 }

当我们运行测试时,我们在控制台中得到以下输出:

FirstParallelUnitTest second() start => ForkJoinPool-1-worker-19
 FirstParallelUnitTest second() end => ForkJoinPool-1-worker-19
 FirstParallelUnitTest first() start => ForkJoinPool-1-worker-19
 FirstParallelUnitTest first() end => ForkJoinPool-1-worker-19

在这个输出中,我们可以注意到两件事。首先,我们的测试按顺序运行。其次,我们使用ForkJoin 线程池。通过启用并行执行,JUnit 引擎开始使用ForkJoin 线程池。

接下来,我们需要添加一个配置来利用这个线程池。我们需要选择一个并行化策略。JUnit 提供了两个实现(dynamicfixeda custom

动态策略根据处理器/内核数乘以因子参数(默认为1)来确定线程数:

junit.jupiter.execution.parallel.config.dynamic.factor

另一方面,固定策略依赖于由以下指定的预定义线程数:

junit.jupiter.execution.parallel.config.fixed.parallelism

要使用自定义策略,我们需要首先通过实现ParallelExecutionConfigurationStrategy

3. 在类内测试并行化

我们已经启用了并行执行并选择了一个策略。现在是在同一个类中并行执行测试的时候了。有两种方法可以配置它。一种是使用@Execution(ExecutionMode.CONCURRENT)

junit.jupiter.execution.parallel.mode.default = concurrent

在我们选择如何配置它并运行我们的FirstParallelUnitTest

FirstParallelUnitTest second() start => ForkJoinPool-1-worker-5
 FirstParallelUnitTest first() start => ForkJoinPool-1-worker-19
 FirstParallelUnitTest second() end => ForkJoinPool-1-worker-5
 FirstParallelUnitTest first() end => ForkJoinPool-1-worker-19

从输出中,我们可以看到两个测试同时在两个不同的线程中启动。请注意,输出可以从一次运行更改为另一次运行。使用ForkJoin 线程池时,这是预期的。

还有一个选项可以在同一线程中FirstParallelUnitTest

4. 测试模块内的并行化

在我们引入一个新属性之前,我们将创建SecondParallelUnitTestFirstParallelUnitTest:

public class SecondParallelUnitTest{
 @Test
 public void first() throws Exception{
 System.out.println("SecondParallelUnitTest first() start => " + Thread.currentThread().getName());
 Thread.sleep(500);
 System.out.println("SecondParallelUnitTest first() end => " + Thread.currentThread().getName());
 }
 @Test
 public void second() throws Exception{
 System.out.println("SecondParallelUnitTest second() start => " + Thread.currentThread().getName());
 Thread.sleep(500);
 System.out.println("SecondParallelUnitTest second() end => " + Thread.currentThread().getName());
 }
 }

在我们在同一批中运行我们的测试之前,我们需要设置属性:

junit.jupiter.execution.parallel.mode.classes.default = concurrent

当我们运行这两个测试类时,我们得到以下输出:

SecondParallelUnitTest second() start => ForkJoinPool-1-worker-23
 FirstParallelUnitTest first() start => ForkJoinPool-1-worker-19
 FirstParallelUnitTest second() start => ForkJoinPool-1-worker-9
 SecondParallelUnitTest first() start => ForkJoinPool-1-worker-5
 FirstParallelUnitTest first() end => ForkJoinPool-1-worker-19
 SecondParallelUnitTest first() end => ForkJoinPool-1-worker-5
 FirstParallelUnitTest second() end => ForkJoinPool-1-worker-9
 SecondParallelUnitTest second() end => ForkJoinPool-1-worker-23

从输出中,我们可以看到所有四个测试在不同的线程中并行运行。

结合我们在本节和上一节中提到的两个属性及其值(same_thread and concurrent

  1. (same_thread, same_thread

  2. (same_thread, concurrent

  3. (concurrent, same_thread

  4. (concurrent, concurrent

5. 同步

在理想情况下,我们所有的单元测试都是独立和隔离的。然而,有时这很难实现,因为它们依赖于共享资源。然后,在并行运行测试时,我们需要同步测试中的公共资源。注解的形式为我们提供了这样的机制。@ResourceLock

同样,和以前一样,让我们创建ParallelResourceLockUnitTest

public class ParallelResourceLockUnitTest{ private List<String> resources; @BeforeEach
 void before() {
 resources = new ArrayList<>();
 resources.add("test");
 }
 @AfterEach
 void after() {
 resources.clear();
 }
 @Test
 @ResourceLock(value = "resources")
 public void first() throws Exception {
 System.out.println("ParallelResourceLockUnitTest first() start => " + Thread.currentThread().getName());
 resources.add("first");
 System.out.println(resources);
 Thread.sleep(500);
 System.out.println("ParallelResourceLockUnitTest first() end => " + Thread.currentThread().getName());
 }
 @Test
 @ResourceLock(value = "resources")
 public void second() throws Exception {
 System.out.println("ParallelResourceLockUnitTest second() start => " + Thread.currentThread().getName());
 resources.add("second");
 System.out.println(resources);
 Thread.sleep(500);
 System.out.println("ParallelResourceLockUnitTest second() end => " + Thread.currentThread().getName());
 }
 }

@ResourceLock允许我们指定共享哪个资源以及我们想要使用的锁类型(默认为 使用当前设置,JUnit 引擎将检测到我们的测试都使用共享资源并按顺序执行它们:ResourceAccessMode.READ_WRITE

ParallelResourceLockUnitTest second() start => ForkJoinPool-1-worker-5
 [test, second]
 ParallelResourceLockUnitTest second() end => ForkJoinPool-1-worker-5
 ParallelResourceLockUnitTest first() start => ForkJoinPool-1-worker-19
 [test, first]
 ParallelResourceLockUnitTest first() end => ForkJoinPool-1-worker-19

六,结论

在本文中,我们首先介绍了如何配置并行执行。接下来,有哪些可用的并行策略以及如何配置多个线程。之后,我们介绍了不同的配置如何影响测试执行。最后,我们介绍了共享资源的同步。


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *