Explaining how the real world works!     RSS Feed The Automated Tester on Twitter The Automated Tester on LinkedIn The AutomatedTester on github

Selenium Page Object Pattern

Tue 22 Jun 2010

In this tutorial we will have a look at how we can apply the Page Object design pattern. This is an extremely useful design pattern for UI tests as it abstracts away how we get to different pages or screens. Page Object design helps us create a DSL for our tests that if something were to change on the page we don't need to change the test, we just need to update the object that represents the page.

A large number of people prefer to write tests in this manner than having to use Selenium IDE. I have to admit that I am one these people. It does require a bit of up front work but once done non-technical can start writing tests in a easy to use manner and never have to understand how everything holds together.

The following example is done in C# and requires that you have NUnit and at least Visual Studio Express installed.

In the example below I have created my Page Objects to work against my site.

  • Create a class file for a page. In this case let's start on the Home Page. When this object is instantiated we are going to navigate to that page. From there we
    public class Home
    {
        private readonly ISelenium _selenium;
        
        /// 
        /// Instantiates a new Home Page object. Pass in the Selenium object created in the test SetUp(). 
        /// When the object in instantiated it will navigate to the root
        /// 
        /// Selenium Object created in the tests
        public Home(ISelenium selenium)
        {
            this._selenium = selenium;
            if (!selenium.GetTitle().Contains("home"))
            {
                selenium.Open("/");
            }
        }
    
        /// 
        /// Navigates to Selenium Tutorials Page. Selenium object wll be passed through
        /// 
        /// SeleniumTutorials representing the selenium_training.htm
        public SeleniumTutorials ClickSelenium()
        {
            _selenium.Click("link=selenium");
            _selenium.WaitForPageToLoad("30000");
            return new SeleniumTutorials(_selenium);
        }
    
        /// 
        /// Click on the blog or blog year and then wait for the page to load
        /// 
        /// blog or blog year
        /// Object representing /blog.* pages
        public Blog ClickBlogYear(string year)
        {
            _selenium.Click("link=" + year);
            _selenium.WaitForPageToLoad("30000");
            return new Blog(_selenium);
        }
        // Add more methods as you need them
    }
    
  • Let's create another object that will represent one of the tutorials on the site
    public class SeleniumXPathTutorial
    {
        private readonly ISelenium _selenium;
    
        public const string FirstInput = "number1";
        public const string SecondInput = "number2";
        public const string Total = "total";
    
        public SeleniumXPathTutorial(ISelenium selenium)
        {
            this._selenium = selenium;
        }
    
        public bool IsInputOnScreen(string locator)
        {
            return _selenium.IsElementPresent(locator);
        }
    }
    
  • Let us now create a test class that will hold all of the tests. We will need to create an object for each of the pages and then use the methods in them our tests. As we can see from the code below, the Selenium parts have been kept to a minimum. In actual fact the only bit that shows Selenium is in the Setup() and Teardown() methods.
    [TestFixture]
    public class SiteTests
    {
        private ISelenium selenium;
        [SetUp]
        public void Setup()
        {
            selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://www.theautomatedtester.co.uk");
            selenium.Start();
        }
    
        [TearDown]
        public void Teardown()
        {
            selenium.Stop();
        }
    
        [Test]
        public void ShouldLoadHomeThenGoToXpathTutorial()
        {
            Home home = new Home(selenium);
            SeleniumTutorials seleniumTutorials = home.ClickSelenium();
            SeleniumXPathTutorial seleniumXPathTutorial = seleniumTutorials.ClickXpathTutorial();
            Assert.True(seleniumXPathTutorial.
    					IsInputOnScreen(SeleniumXPathTutorial.FirstInput));
            Assert.True(seleniumXPathTutorial
    					.IsInputOnScreen(SeleniumXPathTutorial.SecondInput));
            Assert.True(seleniumXPathTutorial
    					.IsInputOnScreen(SeleniumXPathTutorial.Total));
        }
    }
    
  • Now try make a few more tests to try work against the other pages in the site. If you find that you are repeating yourself in the classes split that out again. If you follow DRY (Don't Repeat Yourself) then you will have a set of tests that are extremely maintainable.

By using the Page Object you can make your tests really easy to write and when things change, as they invariably do, you can go from a number of tests failing to suddenly passing with making one change. We have followed good practise in keeping the test code (the asserts) inside the test and not in the page objects. You can also swap out Selenium 1 and put Selenium 2 in its place and not touch the tests.

    Area: tutorials

blog comments powered by Disqus