带有页面对象模型的 Specflow 中的常见步骤定义

Common Step Definitions in Specflow with Page Object Model

我的登录页面绑定中有一个步骤定义

[When(@"I click the '(.*)' button")]
public void IClickTheButton(string buttonName)
{
    LoginPage loginPage = new LoginPage();
    loginPage.ClickTheButton(buttonName);
}

我的页面对象是使用 BasePage 中的 ClickTheButton 方法设置的:

public class LoginPage : BasePage
{
    public LoginPage(IWebDriver _driver)
    {
        driver = _driver;
    }
    // some methods

}
public class HomePage : BasePage
{
    public HomePage(IWebDriver _driver)
    {
        driver = _driver;
    }
    // some methods
}
public class BasePage
{
    //no constructor atm
    public void ClickTheButton(string buttonName)
    {
        driver.GetFirstButtonWithTextContaining(buttonName).Click();
    }
    // more methods
}      

应用程序中的所有按钮的格式都相同,因此 GetFirstButtonWithTextContaining 方法将使用

单击它们
driver.FindElements(By.TagName("button")).Where(x => x.Text == buttonName).First();

问题是我将在我的所有功能文件(如主页功能)中使用“我单击‘(.*)’按钮”,因此使用利用登录页面步骤定义似乎不正确其他页面上所有按钮的登录页面实例 class。

我正在考虑为这些类型的方法创建一个通用步骤 defs 文件,但是当我向 BasePage 添加一个构造函数(与其他页面对象相同 classes)并在通用中执行以下操作时步骤 defs 绑定:

BasePage basePage = new BasePage();
basePage.ClickTheButton(buttonName);

是否有更好的实现方式?...使用 BasePage class 似乎是错误的,但我看不到如何在使用页面对象时跨多个功能共享步骤定义。我正在尝试创建尽可能多的通用步骤以共享所有功能。

您的页面模型并不是真正的页面模型。它们只是用 Selenium 做事的便捷包装器,并没有提供良好的抽象层。

直接在步骤定义中使用扩展方法:

[When(@"I click the '(.*)' button")]
public void IClickTheButton(string buttonName)
{
    driver.GetFirstButtonWithTextContaining(buttonName).Click();
}

或者重写您的页面模型以封装页面上的用户操作:

public class LoginPage
{
    private readonly IWebDriver driver;
    private IWebElement Password => driver.FindElement(By.Id("Password"));
    private IWebElement Username => driver.FindElement(By.Id("Username"));
    private IWebElement LoginButton => driver.FindElement(By.XPath("//button[contains(., 'Log In')]"));

    public LoginPage(IWebDriver driver)
    {
        this.driver = driver;
    }

    public HomePage Login(string username, string password = "test")
    {
        Username.SendKeys(username);
        Password.SendKeys(password);
        LoginButton.Click();

        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));

        wait.Until(ExpectedConditions.StalenessOf(LoginButton));

        return new HomePage(driver);
    }
}

这意味着将您的步骤从程序风格(我点击这个;我做那个)改变为更加行为驱动的风格。例如,通常 "log in" 使用程序步骤:

Given I am on the login page
When I enter "foo" for the "Username"
And I enter "bar" for the "Password"
And I click the "Log In" button

相反,行为驱动的步骤将是一个快速的单行代码,将其大部分行为委托给页面模型:

Given I am logged in as "foo"

[Given(@"I am logged in as ""(.+)""")]
public GivenIAmLoggedInAs(string username)
{
    var loginPage = new LoginPage(driver);

    loginPage.LogIn(username);
}

您的 Cucumber 步骤应该是一层薄薄的单板,将您的功能文件中的应用程序行为描述粘合到页面模型中,该页面模型将该行为封装在页面中。