如何停止以编程方式启动的JUnit 5测试执行流程?
解决方案
需求1:实现测试串行执行
直接使用JDK自带的Executors.newSingleThreadExecutor()创建全局单例的单线程线程池,所有测试执行任务都提交到这个线程池运行,天然保证同一时间只有一个测试处于运行状态,完全避免测试间的互相干扰。OSGi环境下可以将该线程池实例放到公共bundle中作为共享服务,满足多bundle触发测试的串行要求。
需求2:终止运行中的测试/避免永久阻塞
JUnit 5的Launcher.execute()是同步阻塞方法,需要将执行逻辑放到独立线程运行,通过Future接口实现超时自动终止、主动手动终止两个能力:
核心实现代码
首先定义全局共享的执行组件:
// 全局单例单线程执行器,天然保证测试串行 private static final ExecutorService TEST_EXECUTOR = Executors.newSingleThreadExecutor(); // 存储当前运行中的测试任务句柄,用于主动终止 private static Future<?> currentTestFuture;
改造原有测试启动逻辑:
// 原有构造Launcher、测试请求的逻辑保持不变 LauncherConfig launcherConfig = LauncherConfig.builder().addTestEngines( new JupiterTestEngine() ).build(); Launcher launcher = LauncherFactory.create( launcherConfig ); LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() .selectors( selectClass( testName ) ) .build(); TestPlan testPlan = launcher.discover( request ); // 提交测试任务到线程池,自动串行排队 currentTestFuture = TEST_EXECUTOR.submit(() -> { try { launcher.execute(request); } finally { currentTestFuture = null; } }); try { // 设置最大执行时长,示例为1小时,超时后自动终止测试 currentTestFuture.get(1, TimeUnit.HOURS); } catch (TimeoutException e) { // 超时触发终止,给测试线程发中断信号 currentTestFuture.cancel(true); // 此处可扩展自定义逻辑,比如标记测试结果为超时 } catch (InterruptedException | ExecutionException e) { // 处理主动取消、执行异常等场景 Thread.currentThread().interrupt(); }
如果需要测试人员主动触发终止,调用以下逻辑即可:
if (currentTestFuture != null && !currentTestFuture.isDone()) { currentTestFuture.cancel(true); }
注意事项
- 测试代码需要适配中断信号,
Thread.sleep()、Lock.lockInterruptibly()等操作要正确处理InterruptedException,循环逻辑中可增加if (Thread.currentThread().isInterrupted()) return;检查,保证收到中断信号后能尽快退出。 - 如果测试中存在不响应中断的硬件调用、本地IO操作,需要在硬件操作的封装层增加独立超时逻辑,避免底层调用卡死导致线程无法中断。
- OSGi环境下要注意执行器的生命周期,在bundle停止时调用
TEST_EXECUTOR.shutdownNow()主动清理待执行任务,避免内存泄漏。
内容的提问来源于stack exchange,提问作者fish
相关产品推荐
相关产品推荐

