【问题标题】:In Cucumber, is it possible to programmatically get the current step being executed?在 Cucumber 中,是否可以以编程方式获取正在执行的当前步骤?
【发布时间】:2016-09-23 15:25:43
【问题描述】:
Scenario: As a user, I want to login to the system
Given I am on my website
When I enter valid credentials
Then I am taken to the home page

可以使用getName() 函数检索场景名称。有没有办法也让正在执行的步骤(在Java)?我们预计将在日志记录和报告中使用它。

因此,对于上述场景,在执行相应的步骤定义时将返回I am on my website

【问题讨论】:

    标签: java cucumber


    【解决方案1】:

    我认为 CucumberWithSerenity 注册了一个监听器,它存储了当前的步骤名称。

    在你的 Test-Runner 中试试这个:

    //import net.serenitybdd.cucumber.CucumberWithSerenity;
    @RunWith(CucumberWithSerenity.class)
    @CucumberOptions(...
    

    然后在你的步骤中:

    //import net.thucydides.core.model.TestStep;
    //import net.thucydides.core.steps.StepEventBus;
    if (!StepEventBus.getEventBus().isBaseStepListenerRegistered()) {
        return "Unknown"; // CucumberWithSerenity is required.
    } 
    String currentStepDescr = StepEventBus.getEventBus().getCurrentStep()
        .transform(TestStep::getDescription)
        .get();
    

    依赖:

    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-core</artifactId>
        <version>${serenity.version}</version>
    </dependency>
    

    【讨论】:

      【解决方案2】:

      我使用@BeforeStep 和@AfterStep 解决了它。它有点 hacky,所以只有在你知道自己在做什么的情况下才使用它。

      public class StepDefBeginEndLogger {
      
      private int currentStepDefIndex = 0;
      
      @BeforeStep
      public void doSomethingBeforeStep(Scenario scenario) throws Exception {
      
          Field f = scenario.getClass().getDeclaredField("testCase");
          f.setAccessible(true);
          TestCase r = (TestCase) f.get(scenario);
      
          //You need to filter out before/after hooks
          List<PickleStepTestStep> stepDefs = r.getTestSteps()
                  .stream()
                  .filter(x -> x instanceof PickleStepTestStep)
                  .map(x -> (PickleStepTestStep) x)
                  .collect(Collectors.toList());
      
      
          //This object now holds the information about the current step definition
          //If you are using pico container 
          //just store it somewhere in your world state object 
          //and to make it available in your step definitions.
          PickleStepTestStep currentStepDef = stepDefs
                  .get(currentStepDefIndex);
      }
      
      @AfterStep
      public void doSomethingAfterStep(Scenario scenario) {
          currentStepDefIndex += 1;
      }
      

      }

      【讨论】:

      • 我正在尝试这个,但使用最新的黄瓜 io 版本得到以下内容:java.lang.NoSuchFieldException: testCase - 有什么想法吗?
      • 我也更新到黄瓜 4.8 并且它停止工作了。我得到了测试用例的 NoSuchFieldException。我遍历了 getDeclaredFields[] 但唯一的输出是: cucumber.runtime.java.JavaHookDefinition$ScenarioAdaptor.scenario
      • 我猜他们改变了它的工作方式。好吧,无论如何,这是一个黑客行为。也许新版本是否支持正确的方式值得研究。
      • 遗憾的是我不这么认为
      【解决方案3】:

      这是处理框架更改的更新。 “testCase”字段隐藏在“delegate”下。我使用 io.cucumber.java 版本 5.7.0

      public String getStepText(io.cucumber.java.Scenario scenario){      
          String  currentStepDescr = null;
      
          //value currentStepDefIndex is tracked in the another class
          int currentStepDefIndex = OtherClass.getStepIndex();
      
          Field f = scenario.getClass().getDeclaredField("delegate");
          f.setAccessible(true);
          TestCaseState tcs = (TestCaseState) f.get(scenario);
      
          Field f2 = tcs.getClass().getDeclaredField("testCase");
          f2.setAccessible(true);
          TestCase r = (TestCase) f2.get(tcs);
      
              List<PickleStepTestStep> stepDefs = r.getTestSteps()
                      .stream()
                      .filter(x -> x instanceof PickleStepTestStep)
                      .map(x -> (PickleStepTestStep) x)
                      .collect(Collectors.toList());
      
      
              PickleStepTestStep currentStepDef = stepDefs
                      .get(currentStepDefIndex);
              currentStepDescr = currentStepDef.getStep().getText();
              currentStepDefIndex += 1;
              OtherClass.setStepIndex(currentStepDefIndex);
               return currentStepDescr ;
             }
      

      以下是我的 pom.xml 中的依赖项

      <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-core -->
              <dependency>
                  <groupId>io.cucumber</groupId>
                  <artifactId>cucumber-core</artifactId>
                  <version>5.7.0</version>
              </dependency>
      
      
              <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -->
              <dependency>
                  <groupId>io.cucumber</groupId>
                  <artifactId>cucumber-testng</artifactId>
                  <version>5.7.0</version>
              </dependency>
      
      
              <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
              <dependency>
                  <groupId>io.cucumber</groupId>
                  <artifactId>cucumber-java</artifactId>
                  <version>5.7.0</version>
              </dependency>
      
      
              <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-jvm-deps -->
              <dependency>
                  <groupId>io.cucumber</groupId>
                  <artifactId>cucumber-jvm-deps</artifactId>
                  <version>1.0.6</version>
                  <scope>provided</scope>
              </dependency>
      

      【讨论】:

        【解决方案4】:

        我们通过将整个步骤作为参数包装到步骤定义中来解决了这个问题。换句话说,步骤

        Given I am on my website
        

        翻译成

        'Given I am on my website'
        

        步骤定义实际上会接受一个字符串参数,该参数将对应于步骤

            @And("(.*)") //plus something specific to map step
            public void Initialization(String step) throws Exception {
                    //do something with step
            }
        

        【讨论】:

        • 另一个替代品是QAF Gherkin Client,它提供了可用于不同目的的step listeners
        • @user861594 你将如何用黄瓜注册小黄瓜步骤监听器?我实现了接口,但是当我尝试使用黄瓜选项注册它时。它抛出一个异常,说只允许 cucumber.api.Plugin
        • qaf gherkin 客户端支持 gherkin 但不支持 cucumber。所以你需要使用 qaf/testng runner 而不是 cucumber。
        【解决方案5】:

        把这个留在这里以备将来参考......

        当前版本的 Cucumber (4.2.5) 有 BeforeStep 钩子,但只提供对当前运行场景的访问。

        我为提取当前步骤所做的是使用反射来访问该场景中的步骤;

        @BeforeStep
        public void beforeStep(Scenario scn) throws Exception {
            currentStepIndex++;
        
            Field testCaseField = scn.getClass().getDeclaredField("testCase");
            testCaseField.setAccessible(true);
        
            TestCase tc = (TestCase) testCaseField.get(scn);
            Field testSteps = tc.getClass().getDeclaredField("testSteps");
            testSteps.setAccessible(true);
        
            List<TestStep> teststeps = tc.getTestSteps();
            try {
                PickleStepTestStep pts = (PickleStepTestStep) teststeps.get(currentStepIndex);
                getLog().info("########################");
                getLog().info("##########STEP##########");
                getLog().info(pts.getStepText());
                getLog().info("########################");
                currentStepIndex++;
            } catch (Exception ignore) {
            }
        }
        

        唯一的缺点是,您需要一个 int currentStepIndex 在类级别,并且需要为每个 @Before@BeforeStep 添加 1。

        请注意,在 Cucumber 的未来版本中使用此类反射可能无法正常工作,因为 Cucumber 团队可以决定更改其内部结构。

        【讨论】:

        • 您好,您知道是否可以在运行步骤中访问当前场景?我想在步骤中的特定位置通过scenarion.attach(data, "image/png", fileName)截屏。
        【解决方案6】:

        作为新手,不允许发表评论,所以这里有一些信息,假设您使用的是 cucumber-jvm。

        简短回答,不,Cucumber 本身没有读取步骤名称的选项。您可以使用方法名称来识别调用的内容。

        此外,@BEFORE STEP / @AFTER STEP 标签尚不可用,因此您必须为每个步骤定义调用。

        https://github.com/cucumber/cucumber-jvm/pull/838#issuecomment-234110573

        或者像 junit 或 testng 这样的测试框架可以让你访问执行细节——像这样: http://junit.org/junit4/javadoc/4.12/org/junit/rules/TestWatcher.html.

        如果您确实需要步骤名称仅用于报告目的,您可以简单地解析测试框架生成的 xml 报告。

        【讨论】:

          【解决方案7】:

          这些钩子会有所帮助:

          @BeforeStep
          public void beforeStep(Scenario scenario){
            System.out.println(scenario.toString());
          }
          
          @AfterStep
          public void afterStep(Scenario scenario){
            System.out.println(scenario.toString());
          }
          

          【讨论】:

            【解决方案8】:

            您是否询问是否有可能获得一些表明步骤When I enter valid credentials 已执行的日志记录?

            如果是,答案是肯定的。

            Cucumber 本身没有日志记录的概念,因此您必须添加自己喜欢的日志记录框架。由于 Cucumber 不知道通过您最喜欢的日志框架进行日志记录,因此您必须在 Java 中实现的每个步骤中添加一个日志语句。

            我从未见过自己记录日志的必要性。来自 Maven 的执行日志,或者你使用的任何构建工具,对我来说已经足够了很长时间了。

            报告包括执行的步骤,以便涵盖该案例。

            【讨论】:

              【解决方案9】:

              我也有同样的问题。我试图使用 rs79 的答案,但要么我不知道我实际上在用它做什么,要么它不起作用。 Java 给了我一个“AmbiguousStepDefinitionException”或类似的东西。所以我用不同的方式做了。如果您有大量步骤定义,则需要做一些工作,但它可以工作并且非常简单:

              @Then(value = "^The page should navigate to url \"([^\"])\"$", timeout = MAX_TIME)
              public void the_page_should_navigate_to_url(String url) {
                  //below I use a custom class with a static method setStepName() which just sets a string field in the class
                  CustomClass.setStepName("Then The page should navigate to url " + url);
                  //Assert
              }
              

              现在您无需任何复杂的工具即可访问步骤名称。只需使用 get 方法来访问自定义类中的 step 变量。希望对您有所帮助。

              【讨论】:

                【解决方案10】:

                对我来说,使用自我反思来获取注释似乎更简单:

                import java.lang.annotation.Annotation;
                import java.lang.reflect.Method;
                
                @When("^User enters username and password$")
                public void userEntersUsernameAndPassword() throws Throwable{
                    Method callingMethod = new Object() {} .getClass() .getEnclosingMethod();
                    Annotation  myAnnotation = callingMethod.getAnnotations()[0];   
                    System.out.println("myAnnotation=" + myAnnotation);
                

                结果:

                myAnnotation=@cucumber.api.java.en.Given(timeout=0, value=^User is in portal page$)
                

                【讨论】:

                  【解决方案11】:

                  作为一个新手,我无法评论 AndyGee 的答案,但如果你想拥有实际名称,你必须使用 .getName() 方法或 .getUri + .getLine() 来获得类似 id (.getId () 不返回唯一 ID)。

                  @BeforeStep
                  public void beforeStep(Scenario scenario){
                    System.out.println(scenario.getName().toString());
                  }
                  

                  目前我们正在使用 .getUri() 方法并根据子字符串检查 Uri,以便将来更加灵活。

                  【讨论】:

                    【解决方案12】:

                    你可以添加一个类似的步骤

                    When I log in with the user 'user' and the password 'password'
                    

                    并在需要登录时重复此步骤

                    您必须将包含步骤定义的类放在需要登录的每个 Runner 使用的包中。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-03-12
                      • 2021-07-22
                      • 1970-01-01
                      • 2016-09-08
                      • 1970-01-01
                      • 2011-03-16
                      相关资源
                      最近更新 更多