【问题标题】:What's the best way to use Selenium PageObject Design Pattern使用 Selenium PageObject 设计模式的最佳方法是什么
【发布时间】:2011-11-16 10:05:42
【问题描述】:

我正在使用 Selenium 2 Web 驱动程序和 C#.Net 创建测试。在阅读了大量 Selenium 文档后,我仍然不确定如何使用 PageObject 设计模式进行测试。

许多 selenium 示例仅在 Java 中显示,.Net 的 API 绑定并不总是像人们想象的那样相似,这是由于某些语言设置的限制和标准。

在 .Net Selenium Web Driver 中将 PageObject 设计模式与 PageFactory 结合使用的最佳方式是什么?

最终,我希望我的 PageObjects 处理更多功能,而不是使用 PageObject IWebElements 进行 NUnit 测试。

以下是我目前将如何创建测试的示例。

public class LoginPage
{
    private IWebDriver webDriver;

    [FindsBy(How = How.Id, Using = "ctl00_ctl00_ctl00_insideForm_insideForm_content_txtPassword")]
    public IWebElement Password { get; set; }

    [FindsBy(How = How.Id, Using = "ctl00_ctl00_ctl00_insideForm_insideForm_content_cmdSubmit")]
    public IWebElement SubmitButton { get; set; }

    [FindsBy(How = How.Id, Using = "ctl00_ctl00_ctl00_insideForm_insideForm_content_txtUserName")]
    public IWebElement UserName { get; set; }

    public LoginPage() { }

    public LoginPage(IWebDriver webDriver)
    {
        this.webDriver = webDriver;


        if(!webDriver.Url.Contains("Login.aspx"))
        {
            throw new StaleElementReferenceException("This is not the login page");
        }
        PageFactory.InitElements(webDriver, this);
    }

    public HomePage signIn(string username, string password)
    {
        UserName.SendKeys(username);
        Password.SendKeys(password);
        SubmitButton.Submit();

        // Even if i create a NUnit test for this
        // Issue with page loading still occures when I try and return new object
        HomePage homePage = new HomePage(webDriver);
        PageFactory.InitElements(webDriver, homePage);
        return homePage;
    }
}

目前这是我目前正在使用 NUnit 做的事情:

[TestFixture]
public class LoginPageTest : TestBase
{
    private IWebDriver driver;
    private LoginPage loginPage;
    private HomePage homePage;

    [SetUp]
    [Description("Sets up the test fixture page objects and navigates to the login page.")]
    public void SetUp()
    {
        driver = StartDriver();
        Log.Info("Driver started");
        driver.Navigate().GoToUrl("http://" + Environment + ");
        loginPage = new LoginPage();
        PageFactory.InitElements(driver, loginPage);
        //driver.Navigate().Refresh();
    }

    [Test]
    [Description("Enters invalid credentials and asserts that a correct error message is displayed.")]
    public void SubmitFormInvalidCredentials()
    {
        loginPage.UserName.SendKeys("invalid");
        loginPage.Password.SendKeys("invalid");
        loginPage.SubmitButton.Click();
        IWebElement invalidCredentials = driver.FindElement(By.Id("ctl00_ctl00_ctl00_insideForm_insideForm_ctl02_title"));
        Assert.AreEqual("Invalid user name or password", invalidCredentials.Text);
    }

    [Test]
    [Description("Enters valid credentials and asserts that the user is taken to the home page.")]
    public void SubmitFormValidCredentials()
    {
        loginPage.UserName.SendKeys("valid");
        loginPage.Password.SendKeys("valid");
        loginPage.SubmitButton.Click();

        homePage = new HomePage();
        PageFactory.InitElements(driver, homePage);
        Assert.AreEqual("pattest", homePage.Username.Text);
    }

 }

我为 selenium webdriver 设计模式找到的大多数文章和博客文章都与我发现的以前的文章相矛盾。

那么,正确的方法是什么?

最重要的是,我什至尝试了 PageObject 设计模式。

    [Test]
    [Description("Login using PageObject Design Pattern")]
    public void Login()
    {
        loginPage = new LoginPage(driver);
        HomePage signIn = loginPage.SignIn("pattest", "pattest");
    }

在我的登录页面中

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

        if (!driver.Url.Contains("Login.aspx"))
        {
            throw new ElementNotFoundException("This is not the login page");
        }
        PageFactory.InitElements(driver, this);
    }

    public HomePage SignIn(string username, string password)
    {
        UserName.SendKeys(username);
        Password.SendKeys(password);
        SubmitButton.Click();
        return new HomePage(driver);
    }

当然是为了展示我的主页应该如何使用它的构造函数来启动:

public HomePage(IWebDriver d)
    {
        webDriver = d;
        // I need to use this as its not freaking waiting for my Page to load when I pass the webdriver in the consturctor.
        var wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(60));

        try
        {
            wait.Until(driver => driver.FindElement(By.Id("ctl00_ctl00_ctl00_insideForm_insideForm_loginStatus")));
        }
        catch(Exception e)
        {
            throw new ElementNotFoundException("This is not the home page.");
        }
        PageFactory.InitElements(webDriver, this);
    }

如何在测试中有效地使用 WebDriver PageObject 设计模式。 我想不通。

【问题讨论】:

    标签: c# design-patterns webdriver selenium-webdriver pageobjects


    【解决方案1】:

    使用PageFactory.InitElements(_driver, this);在您的基页类的构造函数上:

    public class Page
    {
        public IWebDriver _driver;
    
        public Page(IWebDriver driver)
        {
            this._driver = driver;
            PageFactory.InitElements(_driver, this);
        }
    }
    

    请看PageFactory documentation

    【讨论】:

    • 是的,这是我开始项目时的最终结构。 +1
    • 问题,如果页面对象中的元素是Select(下拉列表),你会怎么做?有人告诉我,您不能将 pageobject 用于 select(dropdownlist),这是真的吗?
    • 有人可以回答@Nick Kahn 的问题吗 :) 我也有同样的问题。
    【解决方案2】:

    我会避免在测试中使用 Asserts 并坚持使用 LoginPage.signIn 方法,该方法会在登录不成功的情况下抛出异常。我不熟悉 NUnit,但我猜它支持“预期失败”的行为。

    最好将依赖于页面的逻辑保存在一个位置(页面类)。

    我想随着主应用程序的发展,您将不得不大量修改 Web UI 测试。

    【讨论】:

    • 感谢您的回复,但这仍然没有解释如何使用页面对象设计模式或为什么我认为使用它的方式会导致诸如 webdriver 忽略页面加载之类的错误,(因为它应该自动执行此操作)以及为什么当我点击提交时它会切断加载页面。在我看来,当我返回一个新的 PageObject 时会发生这种情况,但我仍在传递对同一个 Web 驱动程序的引用,所以我看不出我做错了什么。这整个页面对象设计模式的东西确实需要更多关于 selenium wiki 的文档。 =/
    • 我也同意您对断言的建议,它确实支持预期的异常,一次一步。直到有人能在这个特定问题上启发我
    • 我误解了这个问题,对不起:)
    • 我修复了我遇到的错误,您认为将断言语句移动到我的实际 PageObjects 中是否可以接受?并且所有期望抛出异常的断言都在单元测试中。
    • 我宁愿让我的 PageObjects 抛出异常 - 断言是特定于测试的。
    【解决方案3】:

    创建一个 Browser 类来创建驱动程序和类似的函数,例如用于导航的 GoTo() 和用于关闭浏览器的 Teardown()。`

    public class Browser
        {
            static IWebDriver webDriver = new FirefoxDriver();
            //static IWebDriver webDriver = new ChromeDriver();
            //InternetExplorerOptions  options = new InternetExplorerOptions(); 
            //static IWebDriver webDriver = new InternetExplorerDriver(@"C:\Program Files\Selenium\");
            public static void GoTo(string url)
            {
                //webDriver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 5));
                webDriver.Url = url;
            }
            public static ISearchContext Driver
            {
                get { return webDriver; }
            }
            public static void Teardown()
            {
                webDriver.Quit();
            }   
            public static void MaximizeWindow()
            {
                webDriver.Manage().Window.Maximize();
            }
    

    为页面创建单独的类并使用 PageFactory 来初始化元素。

     public class Admin
        {
            public static AdminPage AdminPage
            {
                get
                {
                    var adminpage = new AdminPage();
                    PageFactory.InitElements(Browser.Driver, adminpage);
                    return adminpage;
                }
    
            }
        }
        public class AdminPage
        {
            string Url = "http://172.18.12.225:4444/admin/admin.aspx";
            string Title = "Login";
            string Text = "Admin";
            public void GoTo()
            {
                Browser.GoTo(Url);
            }
            public bool IsAt()
            {
                return Browser.Title == Title;
            }
            public bool Is_At()
            {
                return Browser.Title == Text;
            }
            [FindsBy(How = How.Id, Using = "ctl16_lblUdpSageMesageCustom")]
            public IWebElement UpdateMessage { get; set; }
    
            [FindsBy(How = How.Id, Using = "hypPreview")]
            public IWebElement BackHomeLink { get; set; }
            //Login
           // [FindsBy(How = How.Id, Using = "ctl14_UserName")]
           // public IWebElement UserNameLink { get; set; }
            [FindsBy(How = How.Id, Using = "ctl14_Password")][CacheLookup]
            public IWebElement PasswordLink { get; set; }
            [FindsBy(How = How.Id, Using = "ctl14_LoginButton")][CacheLookup]
            public IWebElement LoginLink { get; set; }
            //Forgot Password
            [FindsBy(How = How.Id, Using = "ctl14_hypForgotPassword")][CacheLookup]
            public IWebElement FPWLink { get; set; }
            [FindsBy(How = How.Id, Using = "ctl14_wzdForgotPassword_txtUsername")][CacheLookup]
            public IWebElement FPWUserNameLink { get; set; }
            [FindsBy(How = How.Id, Using = "ctl14_wzdForgotPassword_CaptchaValue")][CacheLookup]
            public IWebElement FPWCaptchaLink { get; set; }
            [FindsBy(How = How.Id, Using = "ctl14_wzdForgotPassword_StartNavigationTemplateContainerID_StartNextButton")][CacheLookup]
            public IWebElement FPWNextLink { get; set; }
            [FindsBy(How = How.Id, Using = "ctl14_wzdForgotPassword_StartNavigationTemplateContainerID_CancelButton")][CacheLookup]
            public IWebElement FPWCancelLink { get; set; }
            [FindsBy(How = How.Id, Using = "sfToppane")][CacheLookup]
            public IWebElement TopPane { get; set; }
            [FindsBy(How = How.Id, Using = "sidebar")][CacheLookup]
            public IWebElement sidebar { get; set; }
            //Role
            //[FindsBy(How = How.Id, Using = "ctl19_rptDashBoard_ctl01_hypPageURL")]
            //public IWebElement Role { get; set; }       
            //User
            //[FindsBy(How = How.Id, Using = "ctl19_rptDashBoard_ctl02_hypPageURL")]
            //public IWebElement User { get; set; } 
            public void LogIn(string Username, string Password)
            {
                Browser.MaximizeWindow();
                IWebElement UserNameLink = Browser.WaitForElement(By.Id("ctl14_UserName"), 15);
                UserNameLink.Click();
                UserNameLink.Clear();
                UserNameLink.SendKeys(Username);
                PasswordLink.Click();
                PasswordLink.Clear();
                PasswordLink.SendKeys(Password);
                LoginLink.Click();
            }
    }
    

    这是一个带有登录功能的小例子。 我希望这可能会有所帮助,即使回复晚了。

    【讨论】:

    • 以上是框架,如果您使用Microsoft Visual Studio单元测试框架来创建测试,调用创建的单元测试中的页面功能会更好。
    • 你能分享用于 Browser.WaitForElement 方法的代码吗?另外,如何从通过页面对象创建的 IWebElement 中获取 IWebDriver?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    • 2013-10-17
    • 2011-05-26
    • 2012-07-14
    相关资源
    最近更新 更多