如何 return 将要从步骤定义文件中执行的当前方法

How to return the current method that is going to get executed from step definition file

我需要这个,这样我就可以在执行报告、日志记录等方法之前将所需的参数传递给 class,以便可以并行执行测试用例。我正在将我的关键字驱动的 selenium 框架转换为 BDD Gherkin selenium 框架。在关键字驱动的框架中,我从 Excel 获取关键字,通过 [=14= 将所需的参数传递给 class ] 执行方法前的反射

    String [] argv = new String[]{ 
            "-g"
            ,"businesscomponents"
            ,"./Features"
            //,"-t","@P1,@P2"
            ,"-n","Validate_login_search_using_sku_id"
            };
    ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    byte exitstatus = Main.run(argv, contextClassLoader);

这比我想象的要容易得多。这只需要修改几行 class (Runtime.java) 来找出所有的 hook 和 step 方法。然后,它所需要的只是将 运行 的 dry运行 模式设置为 true 以获取所有详细信息。

覆盖 Runtime.java 行为 -

  1. 从这里下载 1.2.5 class link https://github.com/cucumber/cucumber-jvm/blob/v1.2.5/core/src/main/java/cucumber/runtime/Runtime.java

  2. 在class路径中创建一个包cucumber.runtime,并复制里面的Runtime class。

3.In私有方法运行Hooks(第209行)注释掉第210和214行

    private void runHooks(List<HookDefinition> hooks, Reporter reporter, Set<Tag> tags, boolean isBefore) {   
        //if (!runtimeOptions.isDryRun()) {
            for (HookDefinition hook : hooks) {
                runHookIfTagsMatch(hook, reporter, tags, isBefore);
            }
        //}
    }
  1. 在私有方法运行HookIfTagsMatch(第217行)中,在现有第224行之前添加第210行。基本上如果在dry运行模式下不执行钩子。 try 块包含此代码。 if(!runtimeOptions.isDryRun()) hook.execute(scenarioResult);

自定义格式化程序- 现在,这会将所有详细信息存储在 ThreadLocal 变量中。

public class CustomFormatter implements Reporter, Formatter {

    private Map<String, Map<Scenario, List<StepDefinitionMatch>>> featureDetails;

    private Map<Scenario, List<StepDefinitionMatch>> scenarioDetails;

    private List<StepDefinitionMatch> stepDetails;

    private String featureUri = "";

    private Scenario scenario;

    private Map<Scenario, List<Match>> beforeHooks;

    private Map<Scenario, List<Match>> afterHooks;

    private List<Match> beforeMatches;

    private List<Match> afterMatches;


    public CustomFormatter() {
        featureDetails = new LinkedHashMap<>(); 

        beforeHooks = new LinkedHashMap<>();
        afterHooks = new LinkedHashMap<>();
    }

    @Override
    public void before(Match match, Result result) {
        beforeMatches.add(match);
    }

    @Override
    public void result(Result result) {

    }

    @Override
    public void after(Match match, Result result) {
        afterMatches.add(match);
    }

    @Override
    public void match(Match match) {
        //ThreadLocalStepDefinitionMatch.set((StepDefinitionMatch) match);
        stepDetails.add((StepDefinitionMatch) match);
    }

    @Override
    public void embedding(String mimeType, byte[] data) {
    }

    @Override
    public void write(String text) {
    }

    @Override
    public void syntaxError(String state, String event,
            List<String> legalEvents, String uri, Integer line) {
    }

    @Override
    public void uri(String uri) {
        featureUri = uri;
    }

    @Override
    public void feature(Feature feature) {
        scenarioDetails = new LinkedHashMap<>();
    }

    @Override
    public void scenarioOutline(ScenarioOutline scenarioOutline) {
    }

    @Override
    public void examples(Examples examples) {
    }

    @Override
    public void startOfScenarioLifeCycle(Scenario scenario) {

        this.scenario = scenario;

        stepDetails = new ArrayList<>();
        beforeMatches = new ArrayList<>();
        afterMatches = new ArrayList<>();
    }

    @Override
    public void background(Background background) {
    }

    @Override
    public void scenario(Scenario scenario) {
    }

    @Override
    public void step(Step step) {
    }

    @Override
    public void endOfScenarioLifeCycle(Scenario scenario) {

        scenarioDetails.put(this.scenario, stepDetails);
        beforeHooks.put(this.scenario, beforeMatches);
        afterHooks.put(this.scenario, afterMatches);
    }

    @Override
    public void done() {
    }

    @Override
    public void close() {
        /*System.out.println(featureDetails);
        System.out.println(beforeHooks);
        System.out.println(afterHooks);*/

        StepHookDetails shd = new StepHookDetails(featureDetails, beforeHooks, afterHooks);
        ThreadLocalStepHookDetails.set(shd);
    }

    @Override
    public void eof() {
        featureDetails.put(featureUri, scenarioDetails);
    }
}

如果您取消注释 close() 方法中的打印行,您可以看到类似这样的输出。

步骤方法匹配 - 对应于地图 - Map<String, Map<Scenario, List<StepDefinitionMatch>>> featureDetails in CustomFormatter class

{src/test/resources/features/format2.feature={gherkin.formatter.model.Scenario@38d8f54a=[cucumber.runtime.StepDefinitionMatch@6236eb5f, cucumber.runtime.StepDefinitionMatch@7c1e2a9e], gherkin.formatter.model.Scenario@49e53c76=[cucumber.runtime.StepDefinitionMatch@fa36558, cucumber.runtime.StepDefinitionMatch@672872e1]}}

钩子匹配前-对应地图-Map<Scenario, List<Match>> beforeHooks in CustomFormatter class

{gherkin.formatter.model.Scenario@38d8f54a=[gherkin.formatter.model.Match@32910148], gherkin.formatter.model.Scenario@49e53c76=[gherkin.formatter.model.Match@3f56875e]}

Hook匹配后 - -对应地图 - Map<Scenario, List<Match>> afterHooks in CustomFormatter class

{gherkin.formatter.model.Scenario@38d8f54a=[gherkin.formatter.model.Match@2b4bac49], gherkin.formatter.model.Scenario@49e53c76=[gherkin.formatter.model.Match@fd07cbb]}

这是针对单个功能文件的 运行,如下所示。如果您有多个功能文件,您将在 featureDetails 映射中有多个功能键。钩子将添加到钩子映射中。

Feature: Validating sample Two
    Background:
       Given user gets count from "Car0"

  @Format
  Scenario: Scenario Two
    And user gets count from "Car1"

  @Format
  Scenario: Scenario Two
    And user gets count from "Car1"

重要 - 将三者结合在一起的关键是 Scenario object。很容易迭代 featureDetails 映射并获取 before hook(如果可用),然后是可用步骤,然后是 after hook。 要获取挂钩方法名称,请使用

ThreadLocal Class -

public class ThreadLocalStepHookDetails {

    private static final ThreadLocal<StepHookDetails> threadStepHookDetails = new InheritableThreadLocal<StepHookDetails>();

    private ThreadLocalStepHookDetails() {
    }

    public static StepHookDetails get() {
        return threadStepHookDetails.get();
    }

    public static void set(StepHookDetails match) {
        threadStepHookDetails.set(match);
    }

    public static void remove() {
        threadStepHookDetails.remove();
    }
}

StepHookDetails class -

public class StepHookDetails {

    private Map<String, Map<Scenario, List<StepDefinitionMatch>>> featureDetails;

    private Map<Scenario, List<Match>> beforeHooks;

    private Map<Scenario, List<Match>> afterHooks;

    public StepHookDetails() {}

    public StepHookDetails(
            Map<String, Map<Scenario, List<StepDefinitionMatch>>> featureDetails,
            Map<Scenario, List<Match>> beforeHooks,
            Map<Scenario, List<Match>> afterHooks) {
        this.featureDetails = featureDetails;
        this.beforeHooks = beforeHooks;
        this.afterHooks = afterHooks;
    }

    public Map<String, Map<Scenario, List<StepDefinitionMatch>>> getFeatureDetails() {
        return featureDetails;
    }

    public void setFeatureDetails(
            Map<String, Map<Scenario, List<StepDefinitionMatch>>> featureDetails) {
        this.featureDetails = featureDetails;
    }

    public Map<Scenario, List<Match>> getBeforeHooks() {
        return beforeHooks;
    }

    public void setBeforeHooks(Map<Scenario, List<Match>> beforeHooks) {
        this.beforeHooks = beforeHooks;
    }

    public Map<Scenario, List<Match>> getAfterHooks() {
        return afterHooks;
    }

    public void setAfterHooks(Map<Scenario, List<Match>> afterHooks) {
        this.afterHooks = afterHooks;
    }
}

到店查看详情-

您可以使用以下代码作为示例来查看地图中的详细信息并按顺序查看。即之前的步骤之后。

StepHookDetails sd = ThreadLocalStepHookDetails.get();

        sd.getFeatureDetails().entrySet().stream().forEach(
                e -> e.getValue().entrySet().stream().forEach(
                        ee -> {
                            System.out.println(ee.getKey().getLine());
                            sd.getBeforeHooks().get(ee.getKey()).stream().forEach(bh -> System.out.println(bh.getLocation()));
                            ee.getValue().stream().forEach(s -> System.out.println(s.getLocation() + " " + s.getArguments()));
                            sd.getAfterHooks().get(ee.getKey()).stream().forEach(ah -> System.out.println(ah.getLocation()));
                        }));

上面的特征文件得到了这个输出

Scenario Line Number 8 Before Hook SampleSteps.before() Steps SampleSteps.userGetsCountFromAndStores(String) [Car0] Steps SampleSteps.userGetsCountFromAndStores(String) [Car1] After Hook SampleSteps.afterOne(Scenario) Scenario Line Number 15 Before Hook SampleSteps.before() Steps SampleSteps.userGetsCountFromAndStores(String) [Car0] Steps SampleSteps.userGetsCountFromAndStores(String) [Car1] After Hook SampleSteps.afterOne(Scenario)

这也适用于场景大纲。


已更新 -

Feature: UI Automation

@P1 
Scenario: Validate_Home_page 
Given the customer opens launches the homepage

@Given("^the customer opens launches the homepage$") 
    public void openbrowser() { }

public static void main (String args[]) throws IOException {

        String[] argv = new String[] { "-g", "cucumpar.test.stepdefs", "src/test/resources/features/features.feature", 
                "-t", "@P1" , "-p", "cusform.CustomFormatter","-d"}; 
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 
        byte exitstatus = Main.run(argv, contextClassLoader);
        ----------------
        StepHookDetails sd = ThreadLocalStepHookDetails.get();
        ----------------
    }

结果

{src/test/resources/features/features.feature={gherkin.formatter.model.Scenario@6771beb3=[cucumber.runtime.StepDefinitionMatch@51399530]}} {gherkin.formatter.model.Scenario@6771beb3=[]} {gherkin.formatter.model.Scenario@6771beb3=[]} Scenario Line Number 4 Steps SampleSteps.openbrowser() []


已更新

需要向 StepDefinitionMatch 添加一些代码 class。与您为 Runtime.java 所做的类似,在 - https://github.com/cucumber/cucumber-jvm/blob/v1.2.5/core/src/main/java/cucumber/runtime/StepDefinitionMatch.java 下载 1.2.5 class。将此 class 复制到现有的 cucumber.runtime 包中。 然后添加如下方法--

public List<List<String>> getDataTableData() {

        List<List<String>> data = new ArrayList<>();
        if(step.getRows()!=null)
            step.getRows().forEach(row -> data.add(row.getCells()));
        return data;
    }

就是这样。在现有的打印方法中调用此方法,如下所示。

ee.getValue().stream().forEach(s -> System.out.println("Steps " + s.getLocation() + " " + s.getArguments() + " " + s.getDataTableData()));

对于这个功能文件

 Scenario: Scenario Four
    And car gets count
      | car  | owner      |
      | Car1 | OwnerOne   |
      | Car2 | OwnerTwo   |
      | Car3 | OwnerThree |

得到这个输出 - 第一行是 header,根据需要自定义。

SampleSteps.carGetsCount(DataTable) [] [[car, owner], [Car1, OwnerOne], [Car2, OwnerTwo], [Car3, OwnerThree]]


更新

public Map<String, List<String>> getDataTableData() {

        Map<String, List<String>> mapData = new HashMap<>();
        if(step.getRows()!=null) {
            List<List<String>> data = new ArrayList<>();
            step.getRows().forEach(row -> data.add(row.getCells()));
            List<String> keys = data.remove(0);         
            data.stream().forEach(row -> IntStream.range(0, keys.size()).forEach(index -> {
                List<String> tempList = new ArrayList<>();
                tempList.add(row.get(index));
                mapData.compute(keys.get(index), (key, val) -> {
                    if(val==null)
                        return tempList;
                    val.addAll(tempList);
                    return val;
                });             
            }));            
        }
        return mapData;
    }