Cucumber v7.18.1:如何运行时动态更新步骤状态而非修改JSON报告
解决Cucumber软断言步骤状态不更新问题
针对你用Cucumber-java v7.18.1遇到的软断言步骤状态显示异常问题,这里提供一套在测试运行时动态修改步骤状态的方案,无需事后修改JSON报告:
核心思路
- 自定义软断言类,不仅收集断言错误,还关联错误对应的执行步骤ID
- 通过钩子和ThreadLocal存储当前执行的步骤ID,确保并发测试安全
- 利用Cucumber的
TestCaseState标记出错步骤为失败,替代事后修改报告的方式
具体实现
1. 自定义软断言类
继承AssertJ的SoftAssertions,重写错误收集方法,同时关联当前步骤ID:
import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.SoftAssertionError; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class CucumberSoftAssertions extends SoftAssertions { // 线程本地存储当前步骤ID,支持多线程并发测试 private static final ThreadLocal<String> currentStepId = new ThreadLocal<>(); // 存储步骤ID与对应断言错误的映射 private final Map<String, List<AssertionError>> stepErrors = new HashMap<>(); public static void setCurrentStepId(String stepId) { currentStepId.set(stepId); } public static void clearCurrentStepId() { currentStepId.remove(); } @Override protected void onAssertionErrorCollected(AssertionError error) { String stepId = currentStepId.get(); if (stepId != null) { stepErrors.computeIfAbsent(stepId, k -> new ArrayList<>()).add(error); } super.onAssertionErrorCollected(error); } public Map<String, List<AssertionError>> getStepErrors() { return stepErrors; } @Override public void assertAll() throws SoftAssertionError { try { super.assertAll(); } finally { stepErrors.clear(); } } }
2. 步骤钩子:记录当前步骤ID
在每个步骤执行前,获取并存储当前步骤的唯一ID:
import io.cucumber.java.BeforeStep; import io.cucumber.core.runtime.ScenarioImpl; import io.cucumber.java.Scenario; import io.cucumber.plugin.event.PickleStepTestStep; import io.cucumber.plugin.event.TestStep; public class StepHook { @BeforeStep public void beforeStep(Scenario scenario) { ScenarioImpl scenarioImpl = (ScenarioImpl) scenario; TestStep currentStep = scenarioImpl.getCurrentTestStep(); if (currentStep instanceof PickleStepTestStep) { String stepId = ((PickleStepTestStep) currentStep).getStep().getId(); CucumberSoftAssertions.setCurrentStepId(stepId); } } }
3. 事件监听器:动态修改步骤状态
实现ConcurrentEventListener,监听步骤结束事件,将有断言错误的步骤标记为失败:
import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.EventPublisher; import io.cucumber.plugin.event.PickleStepTestStep; import io.cucumber.plugin.event.TestStepFinished; import io.cucumber.plugin.event.TestStepStarted; import io.cucumber.core.runtime.ScenarioImpl; import io.cucumber.java.Scenario; public class StepStatusUpdater implements ConcurrentEventListener { private final ThreadLocal<Scenario> currentScenario = new ThreadLocal<>(); private final ThreadLocal<CucumberSoftAssertions> softAssertions = ThreadLocal.withInitial(CucumberSoftAssertions::new); @Override public void setEventPublisher(EventPublisher publisher) { publisher.registerHandlerFor(TestStepStarted.class, this::handleTestStepStarted); publisher.registerHandlerFor(TestStepFinished.class, this::handleTestStepFinished); } private void handleTestStepStarted(TestStepStarted event) { currentScenario.set(new ScenarioImpl(event.getTestCase(), event.getTestRun())); } private void handleTestStepFinished(TestStepFinished event) { if (event.getTestStep() instanceof PickleStepTestStep) { PickleStepTestStep step = (PickleStepTestStep) event.getTestStep(); String stepId = step.getStep().getId(); CucumberSoftAssertions assertions = softAssertions.get(); if (assertions.getStepErrors().containsKey(stepId)) { ScenarioImpl scenario = (ScenarioImpl) currentScenario.get(); // 标记当前步骤为失败,传入第一个断言错误作为失败原因 scenario.getTestCaseState().fail(assertions.getStepErrors().get(stepId).get(0)); } CucumberSoftAssertions.clearCurrentStepId(); } } public CucumberSoftAssertions getCurrentSoftAssertions() { return softAssertions.get(); } }
4. 步骤定义与收尾钩子
在步骤中使用自定义软断言,并在场景结束时触发断言汇总:
// 步骤定义类 import io.cucumber.java.en.Then; public class StepDefinitions { private final CucumberSoftAssertions softAssertions = new CucumberSoftAssertions(); @Then("校验数值符合预期") public void validateNumbers() { softAssertions.assertThat(10).isGreaterThan(20); // 故意失败的断言 softAssertions.assertThat(5).isEqualTo(5); // 成功的断言 } } // 场景收尾钩子 import io.cucumber.java.After; import io.cucumber.java.Scenario; public class AfterHook { private final StepStatusUpdater stepStatusUpdater = new StepStatusUpdater(); @After public void afterScenario(Scenario scenario) { stepStatusUpdater.getCurrentSoftAssertions().assertAll(); } }
5. 注册监听器插件
在Cucumber配置中注册StepStatusUpdater,比如在cucumber.properties中添加:
cucumber.plugin=com.yourpackage.StepStatusUpdater,pretty,html:target/cucumber-reports.html
关键说明
- 用ThreadLocal存储步骤ID和软断言实例,保证多线程并发测试的独立性
- 通过
TestCaseState.fail()方法合法修改步骤状态,无需修改原始事件或事后编辑报告 - 自定义软断言类保留了AssertJ的原有功能,同时实现了错误与步骤的关联
内容的提问来源于stack exchange,提问作者DeepakVerma
相关产品推荐
相关产品推荐

