Page Object Model

Page Objects are a way to encapsulate the technical details of a web page and the services it provides so that the developer of a test does not need to delve into the structure of the webpage. Essentially the Page Object acts as an interface between the web page and the testing of that page. The Page Object defines all the elements and actions provided by the web page and the test case uses these to execute the test(s). By clearly delineating the test code from the page objects, you will be able to use the same page objects in a variety of tests cases and achieve code re-use. Another major positive to this approach is that if the application being tested changes then only the Page Object definition needs to be changed in one place and all of the existing test should still work once that change had been made.

I will provide tangible examples later but I used C# to define the Page Object and its properties and methods. The test cases use these objects and methods with NUnit to provide Assertions. This is an important point; the tests, and not the Page Object, should be responsible for making assertions about the state of a page. The Page Object defines the properties and methods you need for creating a test and the test itself uses those properties and methods to make Assertions, allowing us to determine if a test has passed or failed.

The Code

I think the best way to learn a new concept is by getting your hands dirty and trying it out. In this example I will show how to use the Page Object Model to define a Login Page.
Below I will create a page object for Login and I will define some properties (username, password and login button) and a method to login. As the LoginUser() method will take us to a new page, it will return a HomePage object.

using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using NUnit.Framework;

namespace PageObjectModel
{
  public class LoginPage
  {
      private IWebDriver driver;

      [FindsBy(How = How.Id, Using = "tbxUsername")]
      private IWebElement tbxUsername;

      [FindsBy(How = How.Id, Using = "tbxPassword")]
      private IWebElement tbxPassword;

      [FindsBy(How = How.Id, Using = "btnLogin")]
      private IWebElement btnLogin;

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

      public HomePage UserLogin(string user, string pass)
      {
        tbxUsername.SendKeys(user);
        tbxPassword.SendKeys(pass);
        btnLogin.Click();

        return new HomePage(driver);
      }
  }
}

So that’s a very simple Page Object setup, I’ll go through it in more details once we have the code for the test case that uses it:

using NUnit.Framework;
using OpenQA.Selenium;

namespace PageObjectModel
{
  [TestFixture]
  public class Login
  {
    public static IWebDriver driver;

    [SetUp]
    public void Setup()
    {
      driver = new FirefoxDriver()
    }

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

    [Test]
    public static void TestUserLogin(IWebDriver driver)
    {
        LoginPage Login = new LoginPage(driver);
        HomePage Home = Login.LoginUser("testuser", "password");
     }
  }
}

To sumarize the page object code above, we define some WebElements (IWebElements in C#) that we will use and (in this case) one method. We also define an private variable “driver”. This is used to represent the WebDriver object, or the browser itself and the pages it displays. The class constructor uses a passed in variable for the driver object and copies this to the private driver variable that is accessible only within the class. This is a handy trick that means we do not have to pass a driver variable into every method we create as it is already present within our page object. The constructor also initialises our page object and adds the items to the driver.

The notation for defining the page object web elements may look a bit confusing, but we are simply telling the compiler how to Find the webelement by explicitly stating the How and what to use. With the How we can use the same values as with By in a statement such as “driver.FindELment(By.Id(txtPassword))”. We can use ClassName, CssSelector, Custom, Id, LinkText, PartialLinkText, Name, TagName and XPath. The Using part is simply what value to use with the How we have specified.

[FindsBy(How = How.LinkText, Using = "Notes")]

This example tells the compiler to find a web element that has the link text “Notes”. After we have told the compiler how to find our WebElement, we must provide a variable to assign it to.
private IWebElement linkNotes = null;
I assign the variable to null but this is not required.

This short test simply instantiates a LoginPage object called “Login” and then uses the LoginUser() method from the LoginPage Class to test the user logging in. As we are moving to a new page when using the LoginUser() method we use this action to create a new page object to represent the new page. This time it is the Home Page, so we instantiate a HomePage object called “Home”. If we were to continue this test we would be using the proprties and methods available to “Home” as defined in our HomePage class.

References

  • https://code.google.com/p/selenium/wiki/PageObjects
  • http://www.summa-tech.com/blog/2011/10/10/using-page-objects-with-selenium-and-web-driver-20

Leave a Comment