Conveyor belts

How page object patterns can stabilize test automation

Functional graphical user interface (GUI) test automation is hard because the web is constantly evolving to create a better user experience, and the problem is exacerbated by bad information on the web about how to correctly write functional tests.

That’s why most QA automation engineers complain about the "flaky" nature of their tests. But to improve the reliability of your automated functional tests, you first need to accept that the only thing constant in software development is change.

Once you accept that change is inevitable, you can focus on removing possible sources of change from your automation to increase test stability. Go through this exercise, and you will arrive at the page object pattern that removes most of the issues that have been making your tests unstable.

Continuous testing: A practical guide

The automation engineer's biggest complaint: Flaky tests

What is the most common problem automation engineers complain about with respect to functional test automation of the web?

I created this poll to confirm my suspicion that the number-one problem that plagues the test automation community. The results should come as no surprise: Most people complain that functional test automation with the web is flaky.

Twenty-seven percent of respondents complained about flakiness and synchronization issues. Even scarier is the fact that 53% of the automation engineers surveyed can only execute between 1 and 50 functional tests per day, with a 95% accuracy rate. I bet these numbers are inflated, and that a majority of automation engineers actually can only execute between 1 and 10 functional tests with at 95% accuracy per day.  

I'm confident in these numbers simply from experience. At my last three employers, prior to my arrival, the testing teams were able to execute 0, 15, and 10 automated functional tests per day, respectively. Sure, they had more functional tests than that, but I didn't trust them any more than I trust the Russians on a public network at Starbucks. 

Why engineers struggle with test automation stability

So why do so many automation engineers struggle with stable test automation? The reason is actually pretty straight forward, although I personally struggled with this concept for years. Then one day, while reading Robert Martin's Clean Code: A Handbook of Agile Software Craftsmanship, it hit me:  My automated functional GUI tests were WET, as in I wrote everything twice. And, says Martin, "duplication is the primary enemy of a well-designed system,"  so writing everything twice was simply feeding my poorly designed system to become a larger mess.

That's why a practice like page objects is so effective at helping to improve the stability of your automated functional GUI tests. When implemented correctly, page objects help to resolve many problems that make for a poorly designed, automated functional test.

The idea behind page objects: How they can help

The idea behind the page object pattern is straightforward, but their use alone doesn't make them a great idea. With page objects, you use a layer of abstraction between your automated functional tests and the web page to decrease sources of duplication. In other words, you create a single class for a single web page. Then you  use this class in your automated functional test to interact with the web page in the same way you would with the web page manually.

 

Using page objects: Pros and cons

Page objects enforce good object-oriented design principles, such as “don’t repeat yourself” (DRY). A good implementation of a page object helps you to remove duplication and follow the DRY principle. Since you need to interact with a web page using a class, you should encapsulate all of the duplication into methods and properties. Methods help to reuse code. Properties, usually linked to elements on a page, help you to have a single place where a locator for that element can change. 

Using page objects also allows for easy maintenance. Since your test code is now reusable and encapsulated within methods and classes, this makes maintenance easier. Therefore, if you are looking at a sample test like the one below (regarding this page), you can see that you can easily update any element identifiers or methods in a single place.

        [Test]
        public void Test5()
        {
            var complicatedPage = new ComplicatedPage(Driver);
            complicatedPage.GoTo();
            Assert.IsTrue(complicatedPage.IsAt(),
                "The complicated page did not open successfully");

            complicatedPage.CenterContent.OpenToggle();
            Assert.That(complicatedPage.CenterContent.IsToggleOpen(), Is.True);
        }

The test and its steps can remain intact. But if you want to update the implementation of the GoTo() method for example, that lives in a single place, in a single class: the ComplicatedPage.

Therefore, if you have 250 of these functional tests, a single change inside of the GoTo() method will propagate this change through all of your tests.

This creates more robust code, because your tests are easier to maintain. A single change no longer means updating 250 instances. Instead, the paradigm is now that a single update to your code will propagate that change through all the tests that interacted with that method or property.

This approach also creates more readable tests. It's easy to understand what this automated functional test is doing. If you have a basic understanding of coding, you can read a test written using the page object pattern, and understand its purpose. And if you're using the page object pattern correctly, your tests will read like live documentation. This renders the need for actual documentation useless, since your automated functional tests can tell you exactly how the application is supposed to behave.

        [Test]
        public void Test4()
        {
            var complicatedPage = new ComplicatedPage(Driver);
            complicatedPage.GoTo();
            Assert.IsTrue(complicatedPage.IsAt(),
                "The complicated page did not open successfully");

            complicatedPage.LeftSidebar.Search("selenium");
            Assert.That(complicatedPage.IsAt(), Is.False);
        }

Code your tests and page object correctly

Just the fact that you’re using page objects in your functional test automation doesn't necessarily mean that your tests will be more robust. You need to implement the page objects correctly.

I am looking for an excellent Selenium Webdriver with Java instructor to teach students on my website. After asking for a specific code sample of one individual's tests using page objects, this is what I received:

 

This makes me angry — not at the individual, but at the fact that this person was lead to believe that this is the right way to write an automated functional GUI test. This individual, who has nine years of development experience and six years of functional test automation experience under his belt, believes that this is a great test.

I have several problems with this example. First, I have zero understanding about what this functional test does. Second, this test has absolutely no reference to a single page class. Finally, based on my very shallow understanding, it seems as though all of the softValidate() methods are interacting with some kind of HTML property. So when something on the web page inevitably changes, this test, along with 100 others, will need to be updated.

This kind of abomination is all too common. I've seen this kind of code for many years, and I still see it today.

So I implore you, as a true professional, as an employee getting paid to do a great job, to learn the proper way to write a functional test using a page object. There are many great ways to write a functional test using a page object, but the code example above is not one of them. I have an entire course that teaches you how to write functional tests with page objects. I won’t say that my method is the best in the world, as I am always making improvements and learning. But I can definitely say that this test…

        [Test]
        public void Test3()
        {
            var complicatedPage = new ComplicatedPage(Driver);
            complicatedPage.GoTo();
            Assert.IsTrue(complicatedPage.IsAt(), 
                "The complicated page did not open successfully");

            complicatedPage.SocialMediaSection.ClickFirstTwitterButton();
            Assert.That(complicatedPage.IsAt(), Is.False);
        }

...is drastically more robust and easier to understand than the one I received above.

Next steps: Create your own page object pattern

Automated functional test automation with the web is definitely hard, but you can make your life much easier by focusing on the DRY principle. By attempting to remove duplications from your tests, you will naturally begin to create great tests that use the page object pattern.

And if you want to skip the learning curve and jump straight into a good implementation, there are plenty of good examples on the web. When you start applying any of these examples, your functional test automation will see a drastic improvement in it's robustness.

That's my advice for using page object patterns, but I’m always looking for ways to improve. Do you have a useful tip to share that I missed? If so, please post your comments below.

Want to learn more? See Nikolay's presentation, "Using page object pattern to drastically stabilize your automation," during the online Automation Guild conference, which runs January 9-13.

Continuous testing: A practical guide
Topics: Performance