如何使用JUnit Test Engine正确运行扩展测试解决用例重复执行问题
问题原因
双次执行的核心原因是JUnit 5主测试扫描的默认规则会自动识别所有符合命名规范(类名匹配*Test/Test*/*Tests)的静态内部类作为独立测试类执行,这就是gradle test时ExampleExtensionTest被跑第一次的原因;第二次是你在extensionShouldGenerateReport方法里通过EngineTestKit手动触发的。
把静态内部类改成非静态后,非静态内部类的构造函数隐式依赖外部类实例,JUnit不管是主扫描流程还是TestKit引擎都无法直接实例化这个类,自然会出现加载失败的问题。
可用解决方案
选任意一种即可解决问题,优先推荐第一种:
方案1:修改样本类类名(零配置、最简便)
你手动通过selectClass指定TestKit加载的类不受命名规则限制,只要把作为测试样本的内部类改名,避开主扫描的命名匹配规则即可:
- 把
ExampleExtensionTest改成不命中测试命名规则的名字,比如ExampleExtensionSample、ExampleTestFixture都可以 - 同步修改
selectClass里传入的类引用 - 记得给样本类里的测试方法加上
@Test注解(你贴的代码里漏了这个注解,会导致TestKit执行时识别不到测试方法)
修改后的核心代码片段如下:
@Test public void extensionShouldGenerateReport() throws IOException { Path reportPath = Path.of("build/tmp/example-recorded-test"); logger.info("Configured report path: {}", reportPath); EngineTestKit.engine(JupiterEngineDescriptor.ENGINE_ID) .selectors(selectClass(ExtensionTest.ExampleExtensionSample.class)) .execute() .containerEvents() .assertStatistics(stats -> stats.started(1).succeeded(1)); // 断言逻辑保持不变 assertThat(reportPath.toFile().exists()).isTrue(); assertThat(reportPath.resolve("index.html").toFile().exists()).isTrue(); assertThat(reportPath.resolve("metadata.json").toFile().exists()).isTrue(); } @ExtendWith(MyExtension.class) static class ExampleExtensionSample { @Test // 补全缺失的@Test注解 @MyAnnotation(reportPath = "build/tmp/example-recorded-test") public void test() { return; } }
改完后主扫描不会识别这个类,TestKit可以正常加载静态内部类执行,不会再出现双次运行的问题。
方案2:配置Gradle排除指定内部类
如果不想修改类名,可以直接在构建脚本里配置test任务,排除这个样本内部类的扫描,主流程就不会执行它,TestKit手动指定加载不受排除规则影响。
如果是Groovy DSL的build.gradle,添加如下配置:
test { exclude '**/ExtensionTest$ExampleExtensionTest.class' }
如果是Kotlin DSL的build.gradle.kts,添加如下配置:
tasks.test { exclude("**/ExtensionTest\$ExampleExtensionTest.class") }
注意事项
不要用非静态内部类作为TestKit的测试样本:非静态内部类必须绑定外部类实例才能实例化,TestKit执行时没有可用的外部类实例,必然会加载失败。
内容的提问来源于stack exchange,提问作者tftd
相关产品推荐
相关产品推荐

