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

时间:2011-11-16 10:05:43

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

我正在使用Selenium 2 Web Driver和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");
    }

在我的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);
    }

当然,要显示我的HomePage应该如何使用其构造函数启动:

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

3 个答案:

答案 0 :(得分:18)

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

public class Page
{
    public IWebDriver _driver;

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

请参阅PageFactory documentation

答案 1 :(得分:1)

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

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

我猜你必须在主应用程序发展的过程中修改Web UI测试。

答案 2 :(得分:1)

创建一个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();
        }
}

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