如何通过编程从Feature File提取所有Scenario及指定Scenario的assertions?
从Cucumber Feature文件提取Scenario及断言的简便实现
为什么不推荐模式匹配(正则)
别用正则表达式硬匹配,Feature文件有各种语法细节(比如Scenario Outline、嵌套注释、步骤换行、多语言关键词),正则很容易漏判或误判,后期维护成本极高,不如用专门的语法解析工具。
简便实现:用官方Gherkin解析库
Gherkin是Cucumber的官方语法解析库,支持Java、Python、JS等多语言,能准确解析Feature文件的结构化数据,直接提取你需要的内容,是最靠谱的方案。
Python 实现示例
首先安装依赖:
pip install gherkin-official
代码实现:
from gherkin.parser import Parser from gherkin.token_scanner import TokenScanner def parse_feature_file(file_path): with open(file_path, 'r', encoding='utf-8') as f: content = f.read() scanner = TokenScanner(content) parser = Parser() return parser.parse(scanner) # 获取所有Scenario(含Scenario Outline)的名称列表 def get_all_scenarios(feature_data): scenarios = [] for element in feature_data['feature']['children']: if element['type'] == 'Scenario': scenarios.append(element['name']) elif element['type'] == 'ScenarioOutline': scenarios.append(element['name']) return scenarios # 提取指定Scenario的所有断言步骤 # 可根据项目规则调整断言关键词(比如should/assert/verify等) def get_scenario_assertions(feature_data, target_scenario): assertions = [] for element in feature_data['feature']['children']: if element['type'] in ['Scenario', 'ScenarioOutline'] and element['name'] == target_scenario: for step in element['steps']: step_text = step['text'].lower() if any(keyword in step_text for keyword in ['should', 'assert', 'verify', 'be equal to', 'match']): assertions.append(f"{step['keyword']} {step['text']}") break return assertions # 使用示例 if __name__ == '__main__': feature_path = './test.feature' feature = parse_feature_file(feature_path) # 打印所有Scenario print("所有Scenario列表:") for sc_name in get_all_scenarios(feature): print(f"- {sc_name}") # 提取指定Scenario的断言 target = "用户成功登录后跳转至首页" print(f"\n{target} 的断言步骤:") for assert_step in get_scenario_assertions(feature, target): print(f"- {assert_step}")
Java 实现示例
添加Maven依赖:
<dependency> <groupId>io.cucumber</groupId> <artifactId>gherkin</artifactId> <version>26.0.0</version> </dependency>
代码实现:
import io.cucumber.gherkin.GherkinParser; import io.cucumber.gherkin.GherkinTokenScanner; import io.cucumber.messages.types.Feature; import io.cucumber.messages.types.Scenario; import io.cucumber.messages.types.ScenarioOutline; import io.cucumber.messages.types.Step; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Locale; public class FeatureProcessor { // 解析Feature文件 public static Feature parseFeature(String filePath) throws Exception { try (InputStreamReader reader = new InputStreamReader(new FileInputStream(filePath), "UTF-8")) { GherkinTokenScanner scanner = new GherkinTokenScanner(reader); return GherkinParser.parse(scanner).getFeature().get(); } } // 获取所有Scenario名称 public static List<String> getAllScenarioNames(Feature feature) { List<String> names = new ArrayList<>(); feature.getChildren().forEach(child -> { child.getScenario().ifPresent(scenario -> names.add(scenario.getName())); child.getScenarioOutline().ifPresent(outline -> names.add(outline.getName())); }); return names; } // 提取指定Scenario的断言步骤 public static List<String> getAssertionsForScenario(Feature feature, String targetName) { List<String> assertions = new ArrayList<>(); for (Feature.FeatureChild child : feature.getChildren()) { // 处理普通Scenario if (child.getScenario().isPresent()) { Scenario scenario = child.getScenario().get(); if (scenario.getName().equals(targetName)) { collectAssertionSteps(scenario.getSteps(), assertions); break; } } // 处理Scenario Outline else if (child.getScenarioOutline().isPresent()) { ScenarioOutline outline = child.getScenarioOutline().get(); if (outline.getName().equals(targetName)) { collectAssertionSteps(outline.getSteps(), assertions); break; } } } return assertions; } // 筛选断言步骤(可自定义关键词规则) private static void collectAssertionSteps(List<Step> steps, List<String> assertions) { String[] assertionKeywords = {"should", "assert", "verify", "be equal to", "match"}; for (Step step : steps) { String stepText = step.getText().toLowerCase(Locale.ROOT); for (String keyword : assertionKeywords) { if (stepText.contains(keyword)) { assertions.add(step.getKeyword() + step.getText()); break; } } } } public static void main(String[] args) throws Exception { String featurePath = "./src/test/resources/test.feature"; Feature feature = parseFeature(featurePath); // 输出所有Scenario System.out.println("所有Scenario列表:"); getAllScenarioNames(feature).forEach(name -> System.out.println("- " + name)); // 输出指定Scenario的断言 String targetScenario = "用户成功登录后跳转至首页"; System.out.println("\n" + targetScenario + " 的断言步骤:"); getAssertionsForScenario(feature, targetScenario).forEach(step -> System.out.println("- " + step)); } }
关键说明
- 断言的判断逻辑可以根据项目规则灵活调整:比如如果项目里断言步骤统一用
Then关键词,或者有特定业务术语,可以直接匹配这些规则,不用局限于示例里的关键词。 - 官方解析库会自动处理Feature文件的注释、空行、Scenario Outline等特殊情况,比正则表达式稳定得多。
内容的提问来源于stack exchange,提问作者sumit
相关产品推荐
相关产品推荐

