You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)判断是否需要生成令牌。

  1. 实现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;
    }
}
  1. 在Cucumber测试套件类中注册监听器:
import io.cucumber.junit.platform.engine.Cucumber;

@Cucumber(
    plugin = {"com.yourpackage.ApiTestDetectorListener"},
    // 其他配置如features、glue等保持原有设置
)
public class CucumberTestSuite {
}
  1. 在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测试对应的类或方法。

  1. 实现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; // 测试结束后重置状态
    }
}
  1. 在基础测试类中注册扩展:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 03:47:04