Java+Cucumber+JUnit5测试执行前判断是否含API测试的方法咨询
问题描述
我用Cucumber + JUnit5开发Java测试项目,同时承担Web应用的API和UI测试工作。API测试中,除常规Bearer令牌外,还需XSRF令牌作为额外安全层。目前我在BeforeAll钩子中获取XSRF令牌,因为它可复用给所有测试用例,但希望优化逻辑:仅当待执行测试列表中至少包含一个API测试时才生成令牌,仅运行UI测试则跳过生成步骤。当前核心问题是:如何在测试执行开始前,获知通过标签筛选后的测试列表中是否存在API测试?
项目依赖版本
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit-platform.version>1.11.2</junit-platform.version> <maven-surefire-junit-platform.version>3.5.1</maven-surefire-junit-platform.version> <selenium.version>4.25.0</selenium.version> <rest-assured.version>5.5.0</rest-assured.version> <log4j.version>2.24.1</log4j.version> <json.version>20240303</json.version> <cucumber.version>7.20.1</cucumber.version> <serenity.version>4.2.3</serenity.version> <maven-resources-plugin.version>3.1.0</maven-resources-plugin.version> <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> <maven-surefire-plugin.version>3.5.1</maven-surefire-plugin.version> <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version> <maven-failsafe-plugin.version>3.5.1</maven-failsafe-plugin.version> <serenity-maven-plugin.version>4.2.3</serenity-maven-plugin.version> </properties>
解决方案
结合你使用的Cucumber 7.x、JUnit5及Serenity技术栈,以下是几种可行的实现方案:
方案1:利用Cucumber原生TestRunListener监听测试计划
Cucumber提供的TestRunListener接口可在测试启动前获取所有待执行场景信息,通过识别API测试的专属标签(如@api)判断是否需要生成令牌。
- 实现
TestRunListener接口:
import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.EventPublisher; import io.cucumber.plugin.event.TestRunStarted; import io.cucumber.plugin.event.TestCase; public class ApiTestDetectorListener implements ConcurrentEventListener { private static boolean hasApiTests = false; // 替换为你标记API测试的标签 private static final String API_TAG = "@api"; @Override public void setEventPublisher(EventPublisher publisher) { publisher.registerHandlerFor(TestRunStarted.class, this::onTestRunStarted); } private void onTestRunStarted(TestRunStarted event) { // 遍历所有待执行测试用例,检测是否包含API标签 for (TestCase testCase : event.getTestCases()) { if (testCase.getTags().stream().anyMatch(tag -> tag.getName().equals(API_TAG))) { hasApiTests = true; break; // 找到匹配项后终止遍历 } } } public static boolean hasApiTests() { return hasApiTests; } }
- 在Cucumber测试套件类中注册监听器:
import io.cucumber.junit.platform.engine.Cucumber; @Cucumber( plugin = {"com.yourpackage.ApiTestDetectorListener"}, // 其他配置如features、glue等保持原有设置 ) public class CucumberTestSuite { }
- 在
BeforeAll钩子中添加判断逻辑:
import org.junit.jupiter.api.BeforeAll; public class BaseTest { @BeforeAll static void setupBeforeAll() { if (ApiTestDetectorListener.hasApiTests()) { // 调用接口获取并存储XSRF令牌 fetchAndStoreXsrfToken(); } } private static void fetchAndStoreXsrfToken() { // 此处编写你的XSRF令牌获取逻辑,示例: // String xsrfToken = RestAssured.given().get("/xsrf-token").then().extract().path("token"); // 将令牌存入静态变量或上下文供API测试调用 } }
方案2:通过JUnit5 TestPlan获取测试节点信息
JUnit5的TestPlan包含所有待执行测试节点,可自定义扩展来遍历节点,判断是否存在API测试对应的类或方法。
- 实现JUnit5扩展接口:
import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.platform.engine.TestPlan; import org.junit.platform.engine.support.descriptor.ClassSource; public class ApiTestDetectorExtension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource { private static boolean hasApiTests = false; @Override public void beforeAll(ExtensionContext context) { TestPlan testPlan = context.getRoot().getStore(ExtensionContext.Namespace.GLOBAL).get(TestPlan.class); if (testPlan == null) return; // 遍历所有测试节点,检测API测试标识 testPlan.getRoots().forEach(root -> { testPlan.getDescendants(root).forEach(descriptor -> { descriptor.getSource().ifPresent(source -> { // 方式1:检测API测试类的专属注解 if (source instanceof ClassSource classSource) { Class<?> testClass = classSource.getJavaClass(); if (testClass.isAnnotationPresent(ApiTest.class)) { hasApiTests = true; } } // 方式2:检测Cucumber标签映射的测试节点标签 descriptor.getTags().forEach(tag -> { if ("api".equals(tag)) { hasApiTests = true; } }); }); }); }); } public static boolean hasApiTests() { return hasApiTests; } @Override public void close() { hasApiTests = false; // 测试结束后重置状态 } }
- 在基础测试类中注册扩展:
import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(ApiTestDetectorExtension.class) public class BaseTest { @BeforeAll static void setupBeforeAll() { if (ApiTestDetectorExtension.hasApiTests()) { fetchAndStoreXsrfToken(); } } // 其他测试方法及逻辑 }
方案3:结合Serenity生态实现判断
如果你用Serenity管理测试生命周期,可直接借助Serenity的API获取测试场景信息:
import net.serenitybdd.core.Serenity; import net.serenitybdd.junit5.extension.SerenityExtension; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(SerenityExtension.class) public class BaseTest { @BeforeAll static void setupBeforeAll() { boolean hasApiTests = Serenity.getActiveSession().getTestOutcomeSummary().getScenarios().stream() .anyMatch(scenario -> scenario.getTags().contains("@api")); if (hasApiTests) { fetchAndStoreXsrfToken(); } } }
注意事项
- 确保所有API测试都使用统一标签(如
@api),保证识别逻辑的准确性。 - 方案1是Cucumber原生实现,直接对接测试场景信息,推荐优先使用。
- 若项目重度依赖Serenity,方案3更贴合现有生态,无需额外编写监听器。
内容的提问来源于stack exchange,提问作者MR. PROD
相关产品推荐
相关产品推荐

