JUnit5中如何使用PostDiscoveryFilter过滤指定参数的参数化测试用例?
JUnit5 过滤指定参数的参数化测试用例实现方案
核心问题原因
JUnit5 的参数化测试属于测试模板类型,PostDiscoveryFilter 是测试发现阶段生效的过滤器,该阶段只会生成一个
TestTemplateTestDescriptor代表整个参数化测试方法,单个参数对应的具体用例描述符是在测试执行阶段才动态生成的,因此无法通过PostDiscoveryFilter实现单条参数用例的过滤。
以下是三种可落地的实现方案:
方案1:测试代码内使用Assumptions(最简实现,适合无需统一管控的场景)
直接在参数化测试方法开头添加假设校验,不满足条件的用例会自动标记为跳过,不会中断后续用例执行:
import static org.junit.jupiter.api.Assumptions.assumeFalse; @ParameterizedTest @ValueSource(strings = {"0,0", "1,1", "2,1", "3,2", "4,3", "5,5", "6,8"}) void testFibParam(String arg) { // 参数等于"1,1"时跳过当前用例 assumeFalse("1,1".equals(arg), "跳过参数为1,1的用例"); // 原有测试逻辑 if(some_condition){ System.exit(0); } // 其余代码 }
方案2:自定义ExecutionCondition扩展(无入侵,适合全局统一过滤规则的场景)
实现JUnit5的ExecutionCondition扩展,该扩展会在每个测试用例(包括参数化生成的单条用例)执行前触发校验,无需修改原有测试代码:
步骤1:实现过滤规则
import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; public class ParameterizedParamFilter implements ExecutionCondition { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { // 仅过滤参数化测试用例 if (context.getTestMethod().isPresent() && context.getTestMethod().get().isAnnotationPresent(ParameterizedTest.class)) { // 获取当前用例的入参(JUnit 5.6+ 支持getArguments方法) Object[] args = context.getArguments().orElseGet(() -> new Object[0]); if (args.length > 0 && "1,1".equals(args[0])) { return ConditionEvaluationResult.disabled("参数匹配过滤规则,跳过该用例"); } } return ConditionEvaluationResult.enabled("未匹配过滤规则,正常执行"); } }
步骤2:注册扩展
三种注册方式可选:
- 局部生效:在测试类上添加
@ExtendWith(ParameterizedParamFilter.class)注解 - 全局生效:在
junit-platform.properties中添加配置junit.jupiter.extensions.autodetection.enabled=true,并给扩展类添加@AutoService(ExecutionCondition.class)注解(需要引入google-auto-service依赖) - Launcher启动生效:在DiscoveryRequest中添加扩展配置即可。
方案3:自定义TestExecutionListener(适合Launcher启动时动态配置过滤规则的场景)
如果你的测试是通过Launcher代码触发的,该方案和你之前JUnit4的实现逻辑最接近,完全不需要修改原有测试代码,过滤规则可以在启动时动态调整:
步骤1:实现Listener
import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestIdentifier; import org.opentest4j.TestAbortedException; public class ParamSkipListener implements TestExecutionListener { @Override public void executionStarted(TestIdentifier testIdentifier) { // 仅过滤测试用例(排除容器类描述符) if (testIdentifier.isTest()) { // 可直接复用你之前的哈希值判断逻辑,替换以下条件即可:testIdentifier.getUniqueId().hashCode() == 1285025359 if (testIdentifier.getDisplayName().contains("1,1")) { throw new TestAbortedException("参数匹配过滤规则,跳过该用例"); } } } }
步骤2:注册到Launcher
LauncherConfig launcherConfig = LauncherConfig.builder() .enableTestEngineAutoRegistration(false) .enableTestExecutionListenerAutoRegistration(false) .enablePostDiscoveryFilterAutoRegistration(false) .addTestEngines(new JupiterTestEngine()) .addTestEngines(new VintageTestEngine()) // 注册自定义Listener .addTestExecutionListeners(new ParamSkipListener()) .build();
内容的提问来源于stack exchange,提问作者Apathy21
相关产品推荐
相关产品推荐

