logo logo

Design Patterns for High-Quality Automated Tests: Template Method

Design Patterns for High-Quality Automated Tests: Template Method

In the first article from the series, we discussed a strategy to create more maintainable and readable page object models in Selenium WebDriver tests. We used the Fluent Interface design pattern for achieving maximum API usability. Next, we talked about another API usability pattern called Singleton for creating only once page object models and afterward reusing them. In this one, we will discuss issues causing your tests to fail once in a while, such as opening pages fast and not all elements are ready for interaction. If you try to act on them while they are still loading – an error will occur ❌

A standard way to handle the situation is to wait for an element that takes the most time to load on the page and wait for it to be visible before performing any actions. However, we cannot put this logic in the base class since each page’s unique element is different. To handle this, we can use the Template Method design pattern.

Test Case

We will automate the main Bing page. We will write the logic for using the advanced images’ filtering options.

bing-image-filters

After that, we will open the detailed page about specific image results and verify that the correct title and URL are displayed.

bing-image-detailed-page

In the previous article, we had only a single page object called BingMainPage containing all of the logic. However, for this extended scenario, we will create an additional page called ResultDetailedPage. In many cases, we don’t need to navigate to a particular page because it is just part of the workflow, the same way the results page right now is part of the search workflow. We cannot go to it directly only by navigating. This is why we will create two separate base classes in the solution, one for the navigatable pages and another for simpler pages. We will enhance the navigatable pages with the Template Method Design Pattern’s integration so that we are sure the page is fully loaded before we start interacting with it, avoiding any potential errors.

Template Method Design Pattern

Definition

The Template Method design pattern defines a skeleton/structure of an algorithm in a base class and then leaves its specific implementation to the child classes.

UML Class Diagram:

template-method-design-pattern

Participants:

The objects participating in this pattern are:

  • Abstract Class – defines the structure of the algorithm, usually uses abstract protected methods and a public method containing the algorithm.
  • Concrete Class – inherits the abstract class and implements the abstract methods as part of the defined algorithm.

Template Method Design Pattern Implementation

To incorporate the Template Method design pattern, we can include an abstract method in the navigatable page class. Its child classes can implement it and wait for their specific element to be displayed.

First, I created a base class for all pages that we cannot go to directly. We reuse two components – the Driver instance and the GetElements method.

public abstract class WebPage<TElements>
    where TElements : WebElements
{
    protected readonly IWebDriver Driver;

    protected WebPage(IWebDriver driver)
    {
        Driver = driver;
    }

    protected TElements GetElements()
    {
        return (TElements)Activator.CreateInstance(typeof(TElements), new object[] { Driver });
    }
}

Notice that we marked the class as abstract and as generic. The generic parameter is the type of our elements class, which contains all elements locators. Some people call it an element map. We need the generic parameter so that we can adequately implement the GetElements method. You will notice a piece of more complicated code inside the method’s body. It uses the so-called Reflection API for creating an instance of the elements’ type by calling its constructor and passing the Driver instance as a parameter.

NOTE: Reflection API in C# and Java allows you to examine the objects at runtime, manipulate internal properties, dynamically create an instance of a type, invoking its methods, or accessing its fields or properties no matter of their access modifiers.

In the where clause we said that the elements’ type needs to derive from the base class called WebElements. This statement is called a generic constraint.

public abstract class WebElements
{
    protected readonly IWebDriver Driver;

    protected WebElements(IWebDriver driver)
    {
        Driver = driver;
    }
}

Here is how we use it. We use it, for now, to reuse the IWebDriver instance again. In the future, we can add helper methods for easing the finding of elements.

public partial class MainPageElements : WebElements
{
    public MainPageElements(IWebDriver driver)
        : base(driver)
    {
    }

    public IWebElement GetSearchBox()
    {
        return Driver.FindElement(By.Id("sb_form_q"));
    }

    public IWebElement GetResultsCountDiv()
    {
        return Driver.FindElement(By.Id("b_tween"));
    }
   // rest of the elements
}

Shall we discuss now the exciting part about integrating the Template Method Design Pattern? 😉 As we discussed we need a separate base page class for the pages to which we can navigate directly. Such a class will hold a property to the URL of the page and a method for navigating.

public abstract class NavigatableWebPage<TElements> : WebPage<TElements>
    where TElements : WebElements
{
    private WebDriverWait _webDriverWait;

    protected NavigatableWebPage(IWebDriver driver)
        : base(driver)
    {
        _webDriverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
    }

    protected abstract string Url { get; }

    public TPage Open<TPage>()
    {
        Driver.Navigate().GoToUrl(Url);
        WaitForPageLoad();
        return (TPage)Activator.CreateInstance(typeof(TPage), new object[] { Driver });
    }

    protected void WaitForElementToExists(By by)
    {
        var js = (IJavaScriptExecutor)Driver;
        _webDriverWait.Until(ExpectedConditions.ElementExists(by));
    }

    protected abstract void WaitForPageLoad();
}

To incorporate the pattern, we can include an abstract method in the navigatable page class. Its child classes can implement it and wait for their specific element to be displayed. After we open the page, we will wait for it to be fully loaded through the abstract WaitForPageToLoad. Each child class will be responsible for specifying what “fully loaded” means.

The usual approach is to wait for a particular element on the page is visible. This is why I added the helper protected method WaitForElementToExists. We can use it later in the concrete page models to wait for their specific web components to appear. Sometimes instead of waiting for an element to show up, you can wait for all AJAX or Angular requests to finish.

Lastly, the navigatable base class is marked again as abstract and generic. To follow the fluent API we developed in the first article from the series, we also use the Reflection API to create an instance of the page itself. We have the page type since I marked the Open method as generic.

This is how the MainPage changed:

public class MainPage : NavigatableWebPage<MainPageElements>
{
    public MainPage(IWebDriver driver)
        : base(driver)
    {
    }

    protected override string Url => "http://www.bing.com/";

    protected override void WaitForPageLoad()
    {
        WaitForElementToExists(By.Id("sb_form_q"));
    }

    public MainPage Search(string textToType)
    {
        GetElements().GetSearchBox().Clear();
        GetElements().GetSearchBox().SendKeys(textToType);
        GetElements().GetSearchBox().SendKeys(Keys.Enter);
        return this;
    }
   // rest of the code
}

The important part is that we override the URL and set it to the correct one. We also implemented the WaitForPageLoad, where we wait for the main search box to be displayed before starting acting on the page.

Here is how our test looks:

[TestMethod]
public void AssertSearchImageResults()
{
    _bingPage
            .Open<MainPage>()
            .Search("automate the planet")
            .ClickImages()
            .SetSize(Sizes.Large)
            .SetColor(Colors.BlackWhite)
            .SetTypes(Types.Clipart)
            .SetPeople(People.All)
            .SetDate(Dates.PastYear)
            .SetLicense(Licenses.All)
            .ClickImageResult(1);
    _resultDetailedPage.AssertResultTitle("Homepage - Automate The Planet")
        .AssertResultLink("https://www.automatetheplanet.com/")
        .ClickVisitSiteButton();

    Assert.AreEqual("https://www.automatetheplanet.com/", _driver.Url);
}

Summary

Separating the base page class into two separate classes made our tests much more maintainable. But why? 🤔 The maintainability is connected with the future costs of fixing broken tests and adding new logic for automating a new website feature. The refactoring that we made will ease the addition of new logic. If we had left the previous implementation, this would mean that each time you need to change the navigation logic, you would change a single base class but at the same time affect all pages that should not be able to be opened directly. The changes helped us to follow the Single Responsibility Principle more closely as well.

In the next part of the series, we will focus on another common problem in automated tests – test workflows.

For a more detailed overview and usage of many more design patterns and best practices in automated testing, check my book “Design Patterns for High-Quality Automated Tests, C# Edition, High-Quality Tests Attributes, and Best Practices“.  You can read part of three of the chapters here:

Happy Testing! 🚀

Anton S. Angelov

About the author

Anton S. Angelov

CTO and Co-founder of Automate The Planet Ltd, inventor of BELLATRIX Test Automation Framework and MEISSA Distributed Test Runner. I have more than ten years in the field of automated testing. For more than six years I worked as QA architect in two big companies- Telerik (back then the biggest software company in Bulgaria) and later in US company called Progress (similar in size). Part of my job was to design and write scalable test automation framework that more than ten teams had to use. In parallel, I consulted a couple of companies regarding test automation and led several related pieces of training. I was nominated four times for best QA in Bulgaria 2017, 2018 (won), 2019 and 2020.

Author of the book- “Design Patterns for High-Quality Automated Tests, C# Edition, High-Quality Tests Attributes, and Best Practices” (#1 New Release, Best Seller under Quality Control on Amazon)

I am an international conference speaker spoke at events (such as Selenium Conf, Appium Conf, Heisenbug) in Russia, India, Romania, Serbia, Netherlands, Poland, Ukraine and many more. Won a couple of times best paper award. Code Project MVP for 2016-2018 and DZone most valuable blogger with more than 150 articles with over 3 million views. I am writing technical articles each week for Automate The Planet Blog for past five years (250+). Last year the website was mentioned five times as one of the top 10 testing blogs in the world. Articles of mine were published in the previous five editions of the Quality Matters Testing Magazine.

– 280+ Published Articles Automate The Planet
– 120+ Published Articles Code Project
– 60+ Published Articles DZone as Most Valuable Blogger
– 6+ Articles Published in Quality Magazines
– 20+ Given International Conferences Talks
– 2 books published
– 5,000,000+ article views
– 1000 000+ amazing readers for 2020
– Read in 180+ countries

Join TestProject Community

Get full access to the world's first cloud-based, open source friendly testing community. Enjoy TestProject's end-to-end test automation Platform, Forum, Blog and Docs - All for FREE.

Join Us Now  

Leave a Reply

popup image

Test, Deploy & Debug in < 1 hr

Leverage a cross platform open source automation framework for web & mobile testing using any language you prefer, and benefit from built-in dashboards & reports. Free & open source.
Get Started
FacebookLinkedInTwitterEmail