Selenium - Best Practices - Page Object Patter & Page Factory

丶灬走出姿态 提交于 2019-12-11 12:33:00

问题


I've been developing my Project since 1 year from 0. I've reached certain level of 'maintenance' of my Framework and tests. However, each day i have more doubts if i'm using good practices in my Project. Would be great if someone experienced could answer for few my questions. Mostly i have questions for Page Object Patter and Page Factory.

Short description:

My Project is a one-page based application written in C#, angular.js, javascript. Driver is a static instance and it has bunch of additional mehtods(in below code i've only show 2). Each page is a static instance initialized in Pages class. Due to above i don't have to initialize the objects in Tests class.

Questions List:

  1. Is it good approach to Initialize static instance in Pages.cs ? In my opinion the [Test] are more redeable when i'm doing it in such way.

  2. What are the "real" advanteges of using PageObject library? Only the naming of variables? "[FindsBy(How=How.Id)]" ?

  3. What are the "real" advanteges of using PageFactory? Because i didn't find any or for my project it's useless.

In my real Project i have a Base class from which child classes are inheriting and common methods for all child classes are written in PageBase.cs. So i have not problem with duplicated code.

Right now i've implemented Singleton in each Page, so it's similiar to the appraoch used in code below (the difference is only the way of initialization the PageObject)

#region Signleton
private static StartPage instance;

private StartPage()
{
}

public static StartPage Instance
{
    get
    {
        if (instance == null)
        {
            instance = new StartPage();
        }

        return instance;
    }
}
#endregion
  1. However in [Test] i have to use the variable name "Instance" and it's not so readable as the approach with initializing the PageObject in Pages.cs. Do you agree?

Singleton Instance

StartPage.Instance.Search();

Concept of approach:

Browser.cs

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

namespace SeleniumTestFramework
{
    public static class Browser
    {
        public static IWebDriver Driver { get; set; }
        public static bool Initialised { get; set; }

        public static void Initialize()
        {
            string chromeDriverDirectory = @"C:\chromedriver_win32";
            Driver = new ChromeDriver(chromeDriverDirectory);
            Initialised = true;
        }

        public static void Quit()
        {
            Driver.Quit();
            Initialised = false;
        }
    }
}

LoginPage.cs

using OpenQA.Selenium;

namespace SeleniumTestFramework.Pages
{
    public class LoginPage
    {
        private IWebElement screenLogin = Browser.Driver.FindElement(By.Id("onScreenLogin"));

        public void OpenLoginModal()
        {
            screenLogin.Click();
        }
    }
}

LoginModal.cs

using OpenQA.Selenium;

namespace SeleniumTestFramework.Pages
{
    public class LoginPage
    {
        private IWebElement screenLogin = Browser.Driver.FindElement(By.Id("onScreenLogin"));

        public void OpenLoginModal()
        {
            screenLogin.Click();
        }
    }
}

StartPage.cs

using OpenQA.Selenium;

namespace SeleniumTestFramework.Pages
{
    public class StartPage
    {
        private IWebElement surenameInput = Browser.Driver.FindElement(By.CssSelector(".id_surname_startpage_testId + input"));
        private IWebElement searchButton = Browser.Driver.FindElement(By.CssSelector(".search-button.search-customer"));


        public void Search()
        {
            surenameInput.SendKeys("1");
            searchButton.Click();
        }
    }
}

Pages.cs

namespace SeleniumTestFramework.Pages
{
    public static class Pages
    {
        public static LoginPage LoginPage
        {
            get
            {
                var loginPage = new LoginPage();
                return loginPage;
            }
        }

        public static LoginModal LoginModal
        {
            get
            {
                var loginModal = new LoginModal();
                return loginModal;
            }
        }

        public static StartPage StartPage
        {
            get
            {
                var startPage = new StartPage();
                return startPage;
            }
        }
    }
}

Tests.cs

using NUnit.Framework;
using SeleniumTestFramework;
using SeleniumTestFramework.Pages;
using OpenQA.Selenium.Support.PageObjects;
using System.Threading;

namespace SeleniumTest
{
    [TestFixture]
    public class Tests
    {
        [SetUp]
        public void Before()
        {
            if (!Browser.Initialised) Browser.Initialize();
            Browser.Driver.Navigate().GoToUrl("http://localhost:8080/client/");
        }

        [TearDown]
        public void After()
        {
            Browser.Quit(); 
        }

        [Test]
        public void Test_without_static()
        {
            LoginPage loginPage = new LoginPage();
            loginPage.OpenLoginModal();

            LoginModal loginModal = new LoginModal();
            loginModal.Login();

            StartPage startPage = new StartPage();
            startPage.Search();
        }

        [Test]
        public void Test_with_static()
        {
            Pages.LoginPage.OpenLoginModal();
            Pages.LoginModal.Login();
            Pages.StartPage.Search();
        }
    }
}

回答1:


Quite a bit to discuss. Lets make a start.

  1. I suppose that it is a matter of taste.

  2. The real advantages of Page Object frameworks is that you isolate page specific information in one place. This makes finding and maintaining this information straight forward. Code that is common across pages can be placed in helper or module files. This common code is not tied to the specifics of any page so this common code is not impacted when a WebElement's findElement(By) changes. If you think about it, it would be crazy not to do it this way.

  3. PageFactory has three parts. The first two are defined in the page specific class. They are the @FindBy instances that define how to access the useful WebElements defined on that page. These come first which makes them easy to find and maintain. Next come the supported tester actions defined as methods. These methods use the @FindBy instances which makes them unaffected by changes to the FindBy instances.

    public class HomePage { 
      final WebDriver driver;
    
      @FindBy(how = How.NAME, using = "text") private WebElement helloText;
      @FindBy(how = How.NAME, using = "exit") private WebElement exitButton;
    
    public HomePage(WebDriver driver) {
      this.driver = driver;
    }
    
    public void clickExitButton() {
      exitButton.click();
    }
    

    }

3 (cont). Now in the actual test script (step definition file in Cucumber) you invoke those actions by first using PageFactory to initialize a page instance and then invoking the test action methods that you defined against that page instance. So

public class HomePageExitTest extends TestCase {
  WebDriver driver;

  @Before public void setUp() throws Exception {
    driver = new FirefoxDriver();
  }

  @Test public void testHomeExit() throws Exception {
    driver.get("yoursite.com");
    HomePage homePage = PageFactory.initElements(driver, HomePage.class);
    homePage.clickExitButton();
  }

  @After public void tearDown() throws Exception {
    driver.quit();
  }
}

See this tutorial http://www.intexsoft.com/blog/item/34-selenium-webdriver-page-object-pattern-and-pagefactory.html



来源:https://stackoverflow.com/questions/32628593/selenium-best-practices-page-object-patter-page-factory

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!