什么是使用Selenium PageObject设计模式的最佳方式

问题描述:

我使用Selenium 2 Web Driver与C#.Net创建测试。在阅读了很多Selenium文档后,我仍然不确定如何使用PageObject设计模式进行测试。什么是使用Selenium PageObject设计模式的最佳方式

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

在.Net Selenium Web驱动程序中使用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); 
    } 

} 

大部分文章和博客帖子中,我发现了硒的webdriver设计模式发出矛盾,以前的文章,我觉得。

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

为了解决这个问题,我甚至给了PageObject设计模式一个尝试。

​​

里面我LoginPage

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设计模式有效地测试。 我无法弄清楚这一点。

使用PageFactory.InitElements(_driver, this);你的基本页面类的构造函数:

public class Page 
{ 
    public IWebDriver _driver; 

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

请参阅PageFactory documentation

+0

是的,这是我在开始项目时结束的结构。 +1 –

+4

问题,如果元素是pageobject中的“Select”(dropdownlist),你会怎么做?我被告知,你不能使用pageobject进行选择(dropdownlist)是真的吗? –

+0

有人可以回答@尼克卡恩的问题:)我有同样的问题。 –

我会避免在测试中断言并坚持使用LoginPage.signIn方法,该方法会在登录失败时引发异常。我不熟悉NUnit,但我想它支持'预计失败'的行为。

最好将页面相关逻辑保存在一个地方(页面类)。

我猜你必须随着主应用程序的发展而修改Web UI测试。

+1

感谢您的回复,但这仍然不能解释如何使用页面对象设计模式或为什么我会使用它的方式,导致错误,如webdriver忽略页面加载,(因为它应该这样做自动)以及为什么当我点击提交时切断加载页面。 在我看来,它发生在我返回一个新的PageObject的时候,但我还在传递对同一个Web驱动程序的引用,所以我没有看到我做错了什么。 这整个pageobject设计模式的东西真的需要更多的硒维基文档。 =/ –

+0

我也同意你对断言的建议,它一次只支持预期的异常。直到有人可以在这个特殊问题上给我启发 –

+0

我误解了这个问题,对不起:) – Bond

创建一个浏览器类来创建驱动程序和类似的功能,如转到()用于导航和拆解()关闭浏览器。 `

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(); 
     } 
} 

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

+0

以上是针对框架的,如果您使用Microsoft Visual Studio单元测试框架创建测试,并在创建的单元测试中调用页面的功能,将会更好。 – user3687440

+0

你能分享用于Browser.WaitForElement方法的代码吗?另外,如何从通过页面对象创建的IWebElement获取IWebDriver? –