Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在页面对象模型类上创建层次结构?_C#_Selenium_Pageobjects - Fatal编程技术网

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

C# 如何在页面对象模型类上创建层次结构?,c#,selenium,pageobjects,C#,Selenium,Pageobjects,这可能是一个非常奇怪的问题,但我甚至不知道如何命名这个问题。我对C#和Selenium很陌生。我已经对它做了一些编码,我很乐意创建一个(quitemessy)测试,它可以按照我想要的方式运行。我想把我的考试安排得更好一点 我将以此为例说明我想要实现的目标: 在登录页面上,我们可以说我有一个“登录”按钮,该按钮打开一个页面,让我输入我的凭证“用户名”和“密码” 假设我想这样编程:让登录页面包含该页面的所有元素,并且只有一个用于登录页面的元素 测试是这样的: LandingPage.Login.Cl

这可能是一个非常奇怪的问题,但我甚至不知道如何命名这个问题。我对C#和Selenium很陌生。我已经对它做了一些编码,我很乐意创建一个(quitemessy)测试,它可以按照我想要的方式运行。我想把我的考试安排得更好一点

我将以此为例说明我想要实现的目标:

在登录页面上,我们可以说我有一个“登录”按钮,该按钮打开一个页面,让我输入我的凭证“用户名”和“密码”

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

测试是这样的:

LandingPage.Login.Click();
LandingPage.Login.Username("username");
LandingPage.Login.Password("password";
LandingPage.Login.LoginBtn.Click();
var landing = new LandingPage(driver);
var loginPage = landing.clickLogIn();
loginPage.Login();
然后,在登录完成后,我可以进行如下操作:

Database. (everything in the database page). (actions or areas i can navigate to)
在这个类上会有一些层次结构。用户名属于login,登录属于LandingPage,等等。 我见过这种类型的编码被使用。我开始为我的测试创建一个“页面对象模型”,并希望将其分离,这样我就可以使用上面显示的方法。“
LandinPage.cs
”将包含登录链接和其他元素,“
login.cs
”将包含用户名字段、密码和登录按钮,以及其他现在不相关的元素

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


我的问题是,这种格式有名字吗?我很难找到这样的网站,这样我就可以在那里开始学习了。

我很快就把它放在了一起,所以它在技术上可能不是100%正确,但这让你知道如何在登录页面和登录页面之间进行切换

LandingPage.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);
    }
}
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();
        }
    }
}
LoginPage.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");
    }
}
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();
        }
    }
}
那么您的测试将如下所示:

LandingPage.Login.Click();
LandingPage.Login.Username("username");
LandingPage.Login.Password("password";
LandingPage.Login.LoginBtn.Click();
var landing = new LandingPage(driver);
var loginPage = landing.clickLogIn();
loginPage.Login();

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

我读了很多书,但从未完全理解您所说的内容。相反,我建议您阅读您提到的页面对象模型,并坚持下去。多年来,它一直是行业标准

这里有几个链接可以让您从Selenium文档开始

这是马丁·福勒写的。他几乎是现代页面对象的发明者。

对于任何东西,你都必须小心博客,所以答案,等等,在页面对象模型上。并不是每个人都做对了。上面的这些链接很好,我相信你可以找到更多。为了给您一个如何在您的案例中实施它们的快速示例

LandingPage.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);
    }
}
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();
        }
    }
}
LoginPage.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");
    }
}
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
努尼特
NUnit3TestAdapter(如果您希望从Visual Studio中的测试资源管理器运行测试…强烈建议您至少调试测试等)
Selenium.Support
Selenium.WebDriver
Selenium.WebDriver.ChromeDriver(或您想要使用的任何浏览器)

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

  • 操作公开为封装用户交互的方法

  • 使用数据隐藏隐藏内部,如定位器和web元素

  • 页面对象上导致从一个页面转换到另一个页面的公共方法应返回另一个表示另一个页面的页面对象

  • 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");
    

    让登录页面成为登录页面的“子页面”是错误的抽象。它们是独立的页面。一个页面上的操作会将用户引导到另一个页面,该页面由
    navigateLoginPage()
    LogIn(string,string)

    上的返回类型表示。这是测试和页面对象的一个超级简单、无装饰的版本。使用NUnit可以添加很多东西,创建一个BagePage类来保存公共FindElement函数,等等。这将使它更易于使用,创建新的测试,使测试运行更一致,并行运行,等等。POM不需要3。赞成和反对的理由很多。我个人不喜欢它,因为它迫使您编写比实际需要更多的方法(在错误情况下,等等,您需要根据状态返回不同的页面对象),在您使用它时没有提供太多的价值,而且很容易被滥用,因为你的整个测试都是一个单一的链式命令,这使得它不可读,也很难调试。@JeffC:3如果没有#3,你就失去了这种设计模式的强大抽象性。此外,你不应该总是对测试中的所有内容使用链式方法,但这并不意味着它不经常有用。当用户交互有多个结果时,要么为每个结果公开不同的方法,要么创建一个包含所有可能的页面模型的对象,但其中只有一个有对象。其余的可能为空。1)我不相信它有你说的那么强大。我知道它是如何工作的,我已经试过了。我认为它卖过头了。如果你喜欢它并且觉得它有用,那就太好了。2) 为什么有能力连锁而没有连锁?但是,如果你连锁,测试将是