如何在页面对象模型类上创建层次结构?

问题描述

这可能是一个非常奇怪的问题,但我什至不知道如何命名这个问题。我对 C# 和 Selenium 很陌生。我已经对其进行了一些编码,并且我很乐意创建一个以我想要的方式运行的(非常混乱的)测试。我想更好地组织我的测试。

我将以此作为我想要实现的示例:

登录页面上,假设我有一个登录”按钮,该按钮会打开一个页面,让我输入我的凭据“用户名”和“密码”。

假设我想这样编程:登录页面包含该页面的所有元素,一个仅用于登录页面的元素。

测试将是这样的:

LandingPage.Login.Click();
LandingPage.Login.Username("username");
LandingPage.Login.Password("password";
LandingPage.Login.LoginBtn.Click();

然后在登录完成后,我可以有类似的东西:

Database. (everything in the database page). (actions or areas i can navigate to)

这个类会有一些层次结构。用户名属于登录名,登录名属于登陆页面,依此类推。 我已经看到正在使用这种类型的编码。我开始为我的测试创建一个页面对象模型”,并希望将它分开,这样我就可以使用上面显示的方式。 “LandinPage.cs”将包含登录链接以及其他元素,而“Login.cs”将包含用户名字段、密码登录按钮,以及现在不相关的其他元素。>

我也明白这并不是真正的单元测试,而是更多的集成测试。

我的问题是,这种格式有名称吗?我正在努力寻找这样的地方,以便我可以在那里开始学习。

解决方法

我很快把它放在一起,所以它在技术上可能不是 100% 正确的,但这让你对如何在登陆页面和登录页面之间传递有一些想法

登陆页面.cs

public class LandingPage
{

    IWebDriver driver;
    readonly By loginBtn = By.Id("some_id");

    public LandingPage(IWebDriver driver)
    {
        this.driver = driver;
    }
    
    // below is the important piece,the clickLogIn method returns an instance of the LoginPage where you can continue on
    public LoginPage clickLogIn()
    {
        driver.FindElement(loginBtn).Click();
        return new LoginPage(driver);
    }
}

登录页面.cs

public class LoginPage
{
    IWebDriver driver;
    readonly By userName = By.Id("passwordID");
    readonly By passWord = By.Id("userID");

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

    public void Login()
    {
        driver.FindElement(userName).SendKeys("user");
        driver.FindElement(passWord).SendKeys("password");
    }
}

那么您的测试将如下所示:

var landing = new LandingPage(driver);
var loginPage = landing.clickLogIn();
loginPage.Login();

如前所述,不要从字面上理解细节,但 LandingPage.cs 中注释的部分是关于如何从特定页面返回新页面对象的重要部分

,

我读了很多,但从来没有遇到过你在说什么。相反,我建议您阅读您提到的页面对象模型并坚持下去。多年来,它一直是行业标准。

这里有几个链接可以帮助您开始使用 Selenium 文档。

https://www.selenium.dev/documentation/en/guidelines_and_recommendations/page_object_models https://github.com/SeleniumHQ/selenium/wiki/PageObjects

这是马丁福勒写的。他几乎是现代页面对象的发明者。 https://martinfowler.com/bliki/PageObject.html

几乎任何事情,您都必须小心页面对象模型上的博客、SO 答案等。不是每个人都正确地做这件事。上面的这些链接很好,我相信你可以找到更多。为您提供如何在您的案例中实施它们的快速示例,

登陆页面.cs

using OpenQA.Selenium;

namespace SeleniumSandbox.PageObject
{
    public class LandingPage
    {
        IWebDriver Driver;

        // this actually isn't a Login button,it's a "Form Authentication" link but for demo purposes...
        private readonly By _loginButtonLocator = By.CssSelector("a[href='/login']");

        public LandingPage(IWebDriver driver)
        {
            this.Driver = driver;
        }

        public void ClickLogin()
        {
            Driver.FindElement(_loginButtonLocator).Click();
        }
    }
}

登录页面.cs

using OpenQA.Selenium;

namespace SeleniumSandbox.PageObject
{
    public class LoginPage
    {
        IWebDriver Driver;

        private readonly By _loginButtonLocator = By.CssSelector("button");
        private readonly By _passwordLocator = By.Id("password");
        private readonly By _usernameLocator = By.Id("username");

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

        public void Login(string username,string password)
        {
            Driver.FindElement(_usernameLocator).SendKeys(username);
            Driver.FindElement(_passwordLocator).SendKeys(password);
            Driver.FindElement(_loginButtonLocator).Click();
        }
    }
}

SampleTest.cs

using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using SeleniumSandbox.PageObject;

namespace SeleniumSandbox.Tests
{
    public class SampleTest
    {
        [Test]
        public void LoginTest()
        {
            IWebDriver driver = new ChromeDriver();
            driver.Manage().Window.Maximize();
            string url = "http://the-internet.herokuapp.com/";
            driver.Url = url;

            string username = "tomsmith";
            string password = "SuperSecretPassword!";

            new LandingPage(driver).ClickLogin();

            new LoginPage(driver).Login(username,password);
        }
    }
}

这使用由 Selenium 贡献者之一 Dave Haeffner 创建的示例页面站点,并且是一个工作示例。您需要引入以下 NuGet 包。

DotNetSeleniumExtras.WaitHelpers
NUnit
NUnit3TestAdapter(如果您想从 Visual Studio 中的测试资源管理器运行测试...强烈建议至少用于调试测试等)
硒.支持
Selenium.WebDriver
Selenium.WebDriver.ChromeDriver(或任何你想使用的浏览器)

,

Page Object Model 模式是名称。页面对象模型设计模式有三个关键概念:

  1. 操作公开为封装用户交互的方法。

  2. 使用数据隐藏来隐藏内部结构,例如定位器和网络元素。

  3. 页面对象上导致从一个页面转换到另一个页面的公共方法应该返回另一个页面对象代表另一个页面。

LandingPage 类应该有一个导航到登录页面的方法,并且这个方法应该返回一个 LoginPage 对象:

LandingPage landingPage = new LandingPage(driver);
LoginPage loginPage = landingPage.NavigateToLoginPage();

那么 LoginPage 应该有一个方法让用户登录,并返回用户接下来应该转到的页面的页面对象:

landingPage = loginPage.LogIn("username","password");

然后对 LandingPage 对象进行断言。

完整流程可能如下所示:

var landingPage = new LandingPage(driver);
var loginPage = landingPage.NavigateToLoginPage();

landingPage = loginPage.LogIn("username","password");

您还可以使用方法链来收紧您的代码:

var landingPage = new LandingPage(driver).NavigateToLoginPage()
                                         .LogIn("username","password");

将登录页面作为登陆页面的“子页面”是错误的抽象。它们是单独的页面。一个页面上的操作将用户引导到另一个页面,这由 NavigateToLoginPage()LogIn(string,string) 上的返回类型表示。