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

Java语言Cucumber API技术问询:获取特征/标签及运行子集并封装REST接口

Java中Cucumber API实现需求方案

结合你提供的Cucumber 7.16.1版本依赖,完全可以通过Cucumber自身API和JUnit Platform相关工具实现你需要的三个功能,以下是具体实现方案:

1. 获取Feature文件列表及包含的Scenarios

使用Cucumber的FeatureParser和ClasspathResourceLoader加载并解析Feature文件,示例代码如下:

import io.cucumber.core.feature.FeatureParser;
import io.cucumber.core.resource.ClasspathResourceLoader;
import io.cucumber.core.resource.Resource;
import io.cucumber.core.resource.ResourceLoader;
import io.cucumber.messages.types.Feature;
import io.cucumber.messages.types.Scenario;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

public class FeatureScanner {
    public List<FeatureInfo> scanFeatures() {
        List<FeatureInfo> featureInfos = new ArrayList<>();
        ResourceLoader resourceLoader = new ClasspathResourceLoader(getClass().getClassLoader());
        
        // 扫描classpath下所有.feature文件,可指定路径如"classpath:features/**/*.feature"
        resourceLoader.resources("classpath:**/*.feature", resource -> {
            Feature feature = parseFeature(resource);
            List<String> scenarioNames = feature.getScenarioDefinitions().stream()
                    .filter(def -> def instanceof Scenario)
                    .map(def -> ((Scenario) def).getName())
                    .toList();
            featureInfos.add(new FeatureInfo(feature.getName(), resource.getUri().toString(), scenarioNames));
            return true;
        });
        return featureInfos;
    }

    private Feature parseFeature(Resource resource) {
        try {
            return FeatureParser.parse(resource.getUri(), resource.getInputStream());
        } catch (Exception e) {
            throw new RuntimeException("解析Feature文件失败: " + resource.getUri(), e);
        }
    }

    // 自定义DTO用于返回Feature信息
    public static class FeatureInfo {
        private String featureName;
        private String featurePath;
        private List<String> scenarioNames;

        public FeatureInfo(String featureName, String featurePath, List<String> scenarioNames) {
            this.featureName = featureName;
            this.featurePath = featurePath;
            this.scenarioNames = scenarioNames;
        }

        // getter方法
        public String getFeatureName() { return featureName; }
        public String getFeaturePath() { return featurePath; }
        public List<String> getScenarioNames() { return scenarioNames; }
    }
}

2. 获取所有Tags列表

在解析Feature文件的过程中,提取Feature和Scenario级别的Tags并去重,基于上面的FeatureScanner扩展实现:

import java.util.HashSet;
import java.util.Set;

public Set<String> getAllTags() {
    Set<String> tags = new HashSet<>();
    ResourceLoader resourceLoader = new ClasspathResourceLoader(getClass().getClassLoader());
    
    resourceLoader.resources("classpath:**/*.feature", resource -> {
        Feature feature = parseFeature(resource);
        // 添加Feature级Tags
        feature.getTags().forEach(tag -> tags.add(tag.getName()));
        // 添加Scenario级Tags
        feature.getScenarioDefinitions().stream()
                .filter(def -> def instanceof Scenario)
                .map(def -> ((Scenario) def).getTags())
                .flatMap(List::stream)
                .forEach(tag -> tags.add(tag.getName()));
        return true;
    });
    return tags;
}

3. 按名称或Tags运行指定子集

利用JUnit Platform Launcher API,结合Cucumber配置参数指定运行范围,示例代码如下:

import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import io.cucumber.junit.platform.engine.Constants;

import java.util.List;

public class CucumberTestRunner {
    public void runSpecifiedTests(List<String> featurePaths, List<String> tags, List<String> scenarioNames) {
        LauncherDiscoveryRequestBuilder requestBuilder = LauncherDiscoveryRequestBuilder.request();
        
        // 指定要运行的Feature文件路径
        if (!featurePaths.isEmpty()) {
            featurePaths.forEach(path -> requestBuilder.selectors(DiscoverySelectors.selectClasspathResource(path)));
        }
        
        // 配置Tags筛选规则
        if (!tags.isEmpty()) {
            String tagExpression = String.join(" or ", tags.stream().map(tag -> "@" + tag).toList());
            requestBuilder.configurationParameter(Constants.FILTER_TAGS_PROPERTY_NAME, tagExpression);
        }
        
        // 配置Scenario名称筛选(支持正则匹配)
        if (!scenarioNames.isEmpty()) {
            String namePattern = String.join("|", scenarioNames);
            requestBuilder.configurationParameter(Constants.FILTER_NAME_PROPERTY_NAME, namePattern);
        }
        
        LauncherDiscoveryRequest request = requestBuilder.build();
        Launcher launcher = LauncherFactory.create();
        // 可自定义TestExecutionListener收集运行结果
        launcher.execute(request);
    }
}

封装为REST端点

基于Spring Boot创建CucumberController暴露REST接口,示例如下:

import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Set;

@RestController
@RequestMapping("/cucumber")
public class CucumberController {
    private final FeatureScanner featureScanner = new FeatureScanner();
    private final CucumberTestRunner testRunner = new CucumberTestRunner();

    @GetMapping("/features")
    public List<FeatureScanner.FeatureInfo> getFeatures() {
        return featureScanner.scanFeatures();
    }

    @GetMapping("/tags")
    public Set<String> getTags() {
        return featureScanner.getAllTags();
    }

    @PostMapping("/run")
    public void runTests(@RequestBody TestRunRequest request) {
        testRunner.runSpecifiedTests(request.getFeaturePaths(), request.getTags(), request.getScenarioNames());
    }

    // 自定义请求DTO
    public static class TestRunRequest {
        private List<String> featurePaths;
        private List<String> tags;
        private List<String> scenarioNames;

        // getter/setter方法
        public List<String> getFeaturePaths() { return featurePaths; }
        public void setFeaturePaths(List<String> featurePaths) { this.featurePaths = featurePaths; }
        public List<String> getTags() { return tags; }
        public void setTags(List<String> tags) { this.tags = tags; }
        public List<String> getScenarioNames() { return scenarioNames; }
        public void setScenarioNames(List<String> scenarioNames) { this.scenarioNames = scenarioNames; }
    }
}

注意事项

  • 你的pom.xml已包含所有必要依赖:cucumber-java、cucumber-junit-platform-engine、junit-platform-launcher,无需额外添加。
  • 若需要收集测试运行结果,可自定义org.junit.platform.launcher.TestExecutionListener并注册到Launcher中,获取测试的成功/失败状态、耗时等信息。
  • Feature文件路径建议使用classpath相对路径,如features/user/Login.feature。

内容的提问来源于stack exchange,提问作者florin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:26:03