logo logo

Design Patterns for High-Quality Automated Tests: Facade

Design Patterns for High-Quality Automated Tests: Facade

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. Lastly, we discussed how the Template method pattern can help us fight issues related to non-interactable elements.

In this publication, we will focus on another common problem in automated tests – test workflows. The workflow is a series of actions and assertions that need to happen always in almost identical order. In our case with the search workflow – type a search term, apply many filters, open detailed results page, verify correct image information is displayed.

Test Case

We will automate the main Bing page. We will write 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

Let’s quickly review how to tests looked without using any other patterns.

[TestClass]
public class FacadeTests
{
    private IWebDriver _driver;
    private MainPage _mainPage;
    private ResultDetailedPage _resultDetailedPage;

    [TestInitialize]
    public void SetupTest()
    {
        _driver = new ChromeDriver();
        _mainPage = new MainPage(_driver);
        _resultDetailedPage = new ResultDetailedPage(_driver);
    }

    [TestCleanup]
    public void TeardownTest()
    {
        _driver.Quit();
    }

    [TestMethod]
    public void AssertAtpSearchImageResults_NoFacade()
    {
        _mainPage
                .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);
    }

    [TestMethod]
    public void AssertTPSearchImageResults_NoFacade()
    {
        _mainPage
                .Open<MainPage>()
                .Search("testproject.io")
                .ClickImages()
                .SetSize(Sizes.ExtraLarge)
                .SetColor(Colors.ColorOnly)
                .SetTypes(Types.Clipart)
                .SetPeople(People.All)
                .SetDate(Dates.PastWeek)
                .SetLicense(Licenses.All)
                .ClickImageResult(1);
        _resultDetailedPage.AssertResultTitle("TestProject · GitHub")
            .AssertResultLink("https://github.com/testproject-io")
            .ClickVisitSiteButton();

        Assert.AreEqual("https://github.com/testproject-io", _driver.Url);
    }
}

We have two separate tests with the exact same workflow, but we use different data. When we need to test a new search term or apply different filters, we will copy one of the existing tests and change the data. There is one huge drawback of such a solution. If the workflow changes, for example, we move the step with applying filters to the last page or add a new filter type, we need to go through the code and rearrange the steps for all tests.

We can solve the problem in a more elegant way through the usage of the Facade design pattern.

Facade Design Pattern

Definition: 

Facades are classes that ease the use of a large chunk of dependent code. Usually, providing a simplified interface – a couple of methods with fewer parameters, instead of tons of initializations of objects. They hide the complexity of the underlying dependent components and expose only the methods that the user needs.

UML Class Diagram:

facade-design-pattern-uml-class-diagram

Participants:

The classes and objects participating in this pattern are:

  • Facade Class – the main class that the user will use to access the simplified API. It usually contains public methods that use the dependent classes’ logic.
  • Dependent Classes – they hold specific logic which is later used in the facade. They are usually used as parameters in the facade.

Facade Design Pattern Implementation

In our scenario, we can create a facade for performing searches. It will contain a single method called SearchImage that will call the pages’ methods in the right order.

public class ImageSearchFacade
{
    private readonly MainPage _mainPage;
    private readonly ResultDetailedPage _resultDetailedPage;
    private readonly IWebDriver _driver;

    public ImageSearchFacade(IWebDriver driver, MainPage mainPage, ResultDetailedPage resultDetailedPage)
    {
        _driver = driver;
        _mainPage = mainPage;
        _resultDetailedPage = resultDetailedPage;
    }

    public void SearchImage(SearchData searchData, string expectedTitle, string expectedUrl)
    {
        _mainPage
                .Open<MainPage>()
                .Search(searchData.SearchTerm)
                .ClickImages()
                .SetSize(searchData.Size)
                .SetColor(searchData.Color)
                .SetTypes(searchData.Type)
                .SetPeople(searchData.People)
                .SetDate(searchData.Date)
                .SetLicense(searchData.License)
                .ClickImageResult(searchData.ResultNumber);
        _resultDetailedPage.AssertResultTitle(expectedTitle)
            .AssertResultLink(expectedUrl)
            .ClickVisitSiteButton();

        Assert.AreEqual(expectedUrl, _driver.Url);
    }
}

We use composition to hold the dependent pages. We initialize the page objects in the constructor. Also, all data required for the workflow is supplied in the form of facade methods’ parameters.

I grouped most of the parameters we need for the workflow in a single data class called SearchData.

NOTE: Composition over inheritance (or composite reuse principle) in object-oriented programming (OOP) is the principle where classes should achieve polymorphic behavior and code reuse by their composition (by containing instances of other classes that implement the desired functionality) rather than inheritance from a base or parent class. This is especially important in programming languages like C#, where multiple class inheritance is not allowed.

Also, we make assertions inside the function, which may seem that it breaks the Single Responsibility Principle. However, in this particular case, we define the test structure, which is the primary purpose of the method. A better name may be – VerifySearchImage or something similar. You may decide to move the assertion methods to separate facade methods. However, I prefer my facade methods to be more concise since we perform a few verifications after some of the actions, not just at the end of the test.

Shall we examine how the tests will look if we use the new facade? 🧐

[TestClass]
public class FacadeTests
{
    private IWebDriver _driver;
    private MainPage _mainPage;
    private ResultDetailedPage _resultDetailedPage;
    private ImageSearchFacade _imageSearchFacade;

    [TestInitialize]
    public void SetupTest()
    {
        _driver = new ChromeDriver();
        _mainPage = new MainPage(_driver);
        _resultDetailedPage = new ResultDetailedPage(_driver);
        _imageSearchFacade = new ImageSearchFacade(_driver, _mainPage, _resultDetailedPage);
    }

    [TestCleanup]
    public void TeardownTest()
    {
        _driver.Quit();
    }

    [TestMethod]
    public void AssertAtpSearchImageResults_WithFacade()
    {
        var searchData = new SearchData()
        {
            SearchTerm = "automate the planet",
            Size = Sizes.Large,
            Color = Colors.BlackWhite,
            Type = Types.Clipart,
            People = People.All,
            Date = Dates.PastYear,
            License = Licenses.All,
            ResultNumber = 1,
        };

        _imageSearchFacade.SearchImage(
            searchData,
            "Homepage - Automate The Planet",
            "https://www.automatetheplanet.com/");
    }

    [TestMethod]
    public void AssertTPSearchImageResults_WithFacade()
    {
        var searchData = new SearchData()
        {
            SearchTerm = "testproject.io",
            Size = Sizes.ExtraLarge,
            Color = Colors.ColorOnly,
            Type = Types.Clipart,
            People = People.All,
            Date = Dates.PastWeek,
            License = Licenses.All,
            ResultNumber = 1,
        };

        _imageSearchFacade.SearchImage(
            searchData,
            "TestProject · GitHub",
            "https://github.com/testproject-io");
    }
}

As you can see, they became shorter and we were able to hide some of the low-level details. This may be a good or a bad thing. Some engineers may say that this made the readability a bit worse which may be right. If you want to see the exact workflow, now you need to open the facade class file. However, we improved the maintainability significantly since if we need to make a change in the workflow, we can do it in a single place. Another improvement is that the creation of new tests is more straightforward.

Combining Facade with Template Method Design Pattern

In the previous section, we saw how we could reuse the test workflows. However, there might be cases where this solution may not be sufficient. Imagine that we have the current search, yet we decide to redesign it after a year or two. The workflow will stay the same, but since it is possible to use another backend system, the way we apply filters or search items can slightly change.

We may need to support both searches for a while to decide which design suits our users better. If you recall the search facade’s code, you will notice that it depends on specific page objects. They will be different for the new search. At the same time, when we deprecate the old search, we want to change the existing tests as little as possible. How can we prepare for such events? We can combine the two design patterns discussed in this chapter – Template Method and Facade.

Image Search Facade with Template Methods

Instead of having a facade with specific dependent pages, we can create an abstract one that defines the workflow using template methods. Later we can create concrete implementations of the facade – one with the new and one with the old page objects.

public abstract class TemplateMethodFacade
{
    public void SearchImage(SearchData searchData, string expectedTitle, string expectedUrl)
    {
        OpenSearchPage();
        Search(searchData.SearchTerm);
        SetSize(searchData.Size);
        SetColor(searchData.Color);
        SetTypes(searchData.Type);
        SetPeople(searchData.People);
        SetDate(searchData.Date);
        SetLicense(searchData.License);
        OpenImageResults(searchData.ResultNumber);
        AssertResult(expectedTitle, expectedUrl);
    }

    protected abstract void OpenSearchPage();
    protected abstract void Search(string searchTerm);
    protected abstract void SetSize(Sizes size);
    protected abstract void SetColor(Colors color);
    protected abstract void SetTypes(Types type);
    protected abstract void SetPeople(People people);
    protected abstract void SetDate(Dates date);
    protected abstract void SetLicense(Licenses license);
    protected abstract void OpenImageResults(int resultNumber);
    protected abstract void AssertResult(string expectedTitle, string expectedLink);
}

Just like in the ImageSearchFacade, the workflow is defined in the public method SearchImage, but instead of calling the page objects’ methods, we call the abstract template methods.

Concrete Facade Implementation

For the old search and for the new one, we will have separate implementations of the above abstract TemplateMethodFacade. Let’s name the old search implementation ImageSearchFirstVersionFacade.

public class TemplateMethodSearchFacade : TemplateMethodFacade
{
    private readonly MainPage _mainPage;
    private readonly ResultDetailedPage _resultDetailedPage;
    private readonly IWebDriver _driver;

    public TemplateMethodSearchFacade(IWebDriver driver, MainPage mainPage, ResultDetailedPage resultDetailedPage)
    {
        _driver = driver;
        _mainPage = mainPage;
        _resultDetailedPage = resultDetailedPage;
    }

    protected override void OpenSearchPage()
    {
        _mainPage.Open<MainPage>();
    }

    protected override void Search(string searchTerm)
    {
        _mainPage.Search(searchTerm).ClickImages();
    }

    protected override void SetSize(Sizes size)
    {
        _mainPage.SetSize(size);
    }

    protected override void SetColor(Colors color)
    {
        _mainPage.SetColor(color);
    }

    protected override void SetTypes(Types type)
    {
        _mainPage.SetTypes(type);
    }

    protected override void SetPeople(People people)
    {
        _mainPage.SetPeople(people);
    }

    protected override void SetDate(Dates date)
    {
        _mainPage.SetDate(date);
    }

    protected override void SetLicense(Licenses license)
    {
        _mainPage.SetLicense(license);
    }

    protected override void OpenImageResults(int resultNumber)
    {
        _mainPage.ClickImageResult(resultNumber);
    }

    protected override void AssertResult(string expectedTitle, string expectedUrl)
    {
        _resultDetailedPage.AssertResultTitle(expectedTitle)
            .AssertResultLink(expectedUrl)
            .ClickVisitSiteButton();

        Assert.AreEqual(expectedUrl, _driver.Url);
    }
}

As with the initial design of the pattern, the page objects were initialized in the constructor, and we used composition to store the pages needed for the implementation of our method. However, here we don’t define the workflow method SeachImage since we inherit it from the base class. Instead, we only need to override and implement all protected abstract methods, and this is where we call the logic from the dependent page objects. We can create a similar facade for the new search with its own page objects that will be used in the same workflow.

The usage in tests remains identical, we just use the concrete facade – ImageSearchFirstVersionFacade instead of ImageSearchFacade.

Summary

This article from the series discussed how we could reuse test workflows through the Facade design pattern. In the end, we combined both patterns, which allowed us to reuse the test workflow for different versions of the web pages. You can try it, and I believe it will make your tests much more maintainable, less flaky, and save you time creating new tests. However, keep in mind that the test understandability decreased a bit because we are hiding all of the details from the reader.

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

Become a master for Selenium testing

Getting started with web testing? We’ve got you covered with a FREE ready-to-go test automation platform that’s already bundled up with Selenium to simplifying and enhancing your experience.
Get Started
FacebookLinkedInTwitterEmail