【问题标题】:Passing a WebElement parameter from a Cucumber feature into the Step Definition将 Cucumber 功能中的 WebElement 参数传递到步骤定义中
【发布时间】:2016-08-23 12:53:55
【问题描述】:

我正在尝试将 Cucumber 功能文件中的 WebElement(而不是字符串)传递到其相应的步骤定义中。

Scenario: Test
    Given I want to click "myWebElement"

相应的步骤定义将是:

@Given("^I want to click (.*)$")
    public void Test(WebElement we) {
        we.click();
    }

在运行时,我得到以下跟踪:

cucumber.runtime.CucumberException: Don't know how to convert ""myWebElement"" into org.openqa.selenium.WebElement.
Try writing your own converter:

@cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter(WebElementConverter.class)
public class WebElement {}

    at cucumber.runtime.ParameterInfo.convert(ParameterInfo.java:150)
    at cucumber.runtime.StepDefinitionMatch.transformedArgs(StepDefinitionMatch.java:68)
    at cucumber.runtime.StepDefinitionMatch.runStep(StepDefinitionMatch.java:37)
    at cucumber.runtime.Runtime.runStep(Runtime.java:299)
    at cucumber.runtime.model.StepContainer.runStep(StepContainer.java:44)
    at cucumber.runtime.model.StepContainer.runSteps(StepContainer.java:39)
    at cucumber.runtime.model.CucumberScenario.run(CucumberScenario.java:44)
    at cucumber.runtime.model.CucumberFeature.run(CucumberFeature.java:165)
    at cucumber.api.testng.TestNGCucumberRunner.runCucumber(TestNGCucumberRunner.java:63)
    at cucumber.api.testng.AbstractTestNGCucumberTests.feature(AbstractTestNGCucumberTests.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:80)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

有没有可以为这种“转换器”编写的函数?

谢谢。

【问题讨论】:

  • 您不是将 WebElement 传递给步骤定义,而是将一个字符串传递给功能文件。这取决于该字符串参数是 id 还是 name 或任何其他选择器。无论如何,您必须扩展 Transformer 泛型类,因为 RemoteWebElement 类只有一个空的构造函数。
  • 在我的例子中,WebElement 是一个存在于页面对象类中的标识符。换句话说:@FindBy (css="input#ButtonClear") public WebElement ClearButton;我正在从功能文件中传递“ClearButton”

标签: java selenium selenium-webdriver cucumber cucumber-java


【解决方案1】:

首先,您需要问自己为什么要将WebElement 作为参数传递给BDD 框架。 BDD 测试框架通常用于验收测试,因此参数应该是即使利益相关者也可以理解的东西。

现在,为了回答您的问题,Cucumber 使用所谓的 Transformers 来实现这一点。摘自 Cucumber 文档:

公共抽象类 Transformer 扩展 Object 实现 cucumber.deps.com.thoughtworks.xstream.converters.SingleValueConverter 允许将步骤定义参数转换为自定义类型, 让您完全控制该类型的实例化方式。

考虑以下 Gherkin 步骤:

假设今天的日期是“10/03/1985”作为示例,假设我们想要 Cucumber 将子字符串“10/03/1985”转换为 org.joda.time.LocalDate 类:

 @Given("today's date is \"(.*)\"")
 public void todays_date_is(LocalDate d) {
 }   If the parameter's class has a constructor with a single String or Object argument, then Cucumber will instantiate it without

任何进一步的麻烦。但是,在这种情况下,这可能不会给你什么 你要。根据您的区域设置,日期可能是 10 月 3 日或 3 月 10 日!

此时您可以使用自定义转换器。你还必须做 如果您的参数类没有带有单个的构造函数 字符串或对象参数。对于 JODA 时间示例:

 @Given("today's date is \"(.*)\"")
 public void todays_date_is(@Transform(JodaTimeConverter.class) LocalDate d) {
 }   And then a JodaTimeConverter class:


 public static class JodaTimeConverter extends Transformer<LocalDate> {
     private static DateTimeFormatter FORMATTER = DateTimeFormat.forStyle("S-");

     @Override
     public LocalDate transform(String value) {
         return FORMATTER.withLocale(getLocale()).parseLocalDate(value);
     }
 }   An alternative to annotating parameters with Transform is to annotate your class with XStreamConverter:

 @XStreamConverter(MyConverter.class)
 public class MyClass {
 }   This will also enable a DataTable to be transformed to a List<MyClass;>

我自己没有尝试过WebElement 类型,但原理是一样的。 您可以找到更多信息here

【讨论】:

  • 谢谢。即使我创建了一个 Transformer,将 String 转换为 WebElement 标识符的固有问题仍然存在。
  • 它似乎适用于任何自定义类型,因此您可能需要更详细地检查您的转换器。基本上是一个简单的返回 driver.findElement(By.id(idVariable));应该做的伎俩。
  • 驱动对象应该如何传递?
  • 你的转换器和测试方法代码是什么样子的?你尝试了什么?
  • public class WebElementConverter extends Transformer { @Override public WebElement transform(String value) { return driver.findElement(By.id(value)); } }
【解决方案2】:

我知道这个问题是针对 java 的,但这是我在 C# 中针对相同要求所做的。我添加只是为了帮助任何来这里寻找相同问题的 c# 解决方案的人

您可以在 c# cucumber/specflow 中使用[StepArgumentTransformation] 实现这一目标

  Scenario Outline: examples with step argument
  Given we have '<Webelements>'

  Examples:
  | Webelements | 
  |     Gmail   |

步骤定义为:

  using TechTalk.SpecFlow;
  using OpenQA.Selenium.Chrome;
  using OpenQA.Selenium.Support.UI;
  using NUnit.Framework;

[Binding]
   public class Browser
  {
     private readonly BrowserDriver _browserDriver;
     public static  OpenQA.Selenium.IWebDriver driver ;

      public Browser(BrowserDriver browserDriver, FeatureContext featureContext)
    {
        _browserDriver = browserDriver;  //your chrome driver
        driver = _browserDriver._driver;
        _featureContext = featureContext;
        _featureContext.Add("driver", driver);
    }

     [Given(@"we have '(.*)'")] 

    public void webelements(ChromeWebElement o)
    {
       Assert.False(o.Displayed);
    }

    [StepArgumentTransformation]
      public ChromeWebElement convertToWebElement(string c)
      {
        ChromeDriver parent = _featureContext.Get<ChromeDriver>("driver");
        return new ChromeWebElement(parent, c);
    }  

显然你会得到结果element is not attached to the page document,因为我只是使用Gmail 元素进行测试

【讨论】:

    【解决方案3】:

    实现这一点的标准方法是提供定位器或定位器的引用作为步骤参数,并使用步骤中的创建元素。例如,使用qaf,您可以执行如下步骤:

        import static com.qmetry.qaf.automation.ui.webdriver.ElementFactory.$;
        ...
    
        @Given("^I want to click (.*)$")
        public void click(String eleLoc) {
            $("eleLoc").click();
        }
    
        //blow is example using qaf step annotation
        //that doesn't need regex...
        @QAFTestStep(description="I want to click {element}")
        public void click(String eleLoc) {
            $("eleLoc").click();
        }
    

    以上步骤可以在特征文件中调用如下:

    Scenario: Test
        Given I want to click "name=q"
    
    

    如果您使用的是locator repository,它可能如下所示:

    mypage_locators.properties

    page.myWebElement=name=q 
    #add other locators for this page
    

    下面是self descriptive value 上面的同一个定位器

    page.myWebElement={"locator":"name=q", "desc":"search text box on search page"}
    

    可以在功能文件中调用步骤如下:

    Scenario: Test
        Given I want to click "page.myWebElement"
    
    

    您可以使用或参考适用于 webmobileperfectoweb-services 的内置步骤,也可以使用自定义步骤实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多