Strategy Design Pattern for Efficient Test automation framework Design

Design Patterns in Automation Testing

Anton Angelov, an automation developer, has been working on a series of posts called Design Patterns in Automation Testing since April.  Using Selenium WebDriver and C# he presents the idea of incorporating these design patterns into automated tests.

Page Object Pattern

With the Page Object Pattern, all elements, actions and validations happening on a page is contained in one single object. Anton uses the Bing search http://www.bing.com/ page as an example. Web Elements such as the SearchBox, GoButton, ResultsCount and actions such as Navigate, Search, and ValidateResults can all be stored in a class called BingMainPage.

This way, you can separate how you interact with the Document Object Model (the Page Object) with the tests themselves (The Test Class).

For locating the web elements, Anton gives two examples of the Page Object, one using PageFactory and one not using the built-in Selenium module, Page Factory.

For the second example, instead of PageFactory it uses:
  • "Page Object Element Map – Contains all element properties and their location logic.
  • "Page Object Validator – Consists of the validations that will be performed on the page.
  • "Page Object (BingMainPage)- Holds the actions that can be performed on the page like Search and Navigate. Exposes an easy access to the Page Validator through the Validate() method. The best implementations of the pattern hide the usage of the Element Map, wrapping it through all action methods.
  • "UI Tests (BingTests) – This class contains a group of tests related to the above page; it can hold only a single instance of the page object".

There does seem to be some discussion in the comments section with this second method, if asserts, such as assertTrue in JUnit or TestNG, should be placed only in the test, not the page object itself.

Advanced Page Object Patterns 

In order to follow the object oriented principle of DRY (Don't Repeat Yourself), thisblog post walks the automater through creating a Driver class which has the static methods StartBrowser, StopBrowser, and BrowserWait.

Now that Anton developed this new class, the earlier work can be refactored.

Facade Design Pattern

To make a purchase, Anton created a class, PurchaseFacade which groups together the page objects for Checkout, ItemPages, ShippingAdderess, and SignIn.

Singleton Design Pattern 


With the Singleton Design Pattern, Anton goes into topics to use with the Base page class such as:

  • Non-thread-safe BaseSingleton Class
  • Thread-safe BaseSingleton Class with Lock
  • Thread-safe BaseSingleton Class with Lazy
  • Thread-safe BaseSingleton Class with Nested Classes

Fluent Page Object Pattern

With this pattern, Anton chains methods, so a test can look like:
    public void SearchForImageFluent()    {        P.BingMainPage.Instance        .Navigate()         .Search("facebook")         .ClickImages()        .SetSize(Sizes.Large)        .SetColor(Colors.BlackWhite)        .SetTypes(Types.Clipart)              .SetPeople(People.All)              .SetDate(Dates.PastYear)                .SetLicense(Licenses.All);    }
======================

WebDriver factory

WebDriver factory is responsible for instantiating the WebDriver based on condition which browser we want to run our tests with.
 public class WebDriverFactory
    {
        public IWebDriver CreateInstance(Browsers browser)
        {
            if (Browsers.Chrome == browser)
            {
                return new ChromeWebDriver();
            }
            else if (Browsers.IE == browser)
            {
                return new InternetExplorerWebDriver();
            }
else if (Browsers.Opera == browser)
            {
                return new OperaWebDriver();
            }
            else
            {
                return new FirefoxWebDriver();
            }
        }

Constructor take an argument browser type. Browser type is defined as enumerationThis is very important. Avoid passing back and forth strings. Always stick to enums or special purpose classes. This will save you time investigating bugs in your automation
public enum Browsers
{
    Chrome, IE, Firefox, Opera
}

Page objects

Everything start by defining proper page objects. There is no fixed recipe for this. It all depends on the structure of application under test. General rule is that repeating elements (header, footer, menu, widget, etc) are extracted as separate objects. The whole idea is to have one element defined in only one place (stay DRY)! Bellow is our HomePage object. What you can do generally is make search and clear search terms. Note that clearing is done with jQuery code. This is because of a bug I’ve described with workaround in Selenium WebDriver cannot click UTF-8 icons post.
class HomePageObject
    {
        private WebDriverFacade webDriver;
        public HomePageObject(WebDriverFacade webDriver)
        {
            this.webDriver = webDriver;
        }
 
        private IWebElement SearchField
        {
            get { return webDriver.FindElement(By.Id("search")); }
        }
 
        public void SearchFor(string text)
        {
            SearchField.SendKeys(text);
        }
 
        public void ClearSearch()
        {
            webDriver.ExecuteJavaScript("$('span.cancel').click()");
        }
    }

NullWebElement

This is null object pattern and implements IWebElement  Interface. There is a NULL property that is used to compare is given element is not found or no
public class NullWebElement extendsIWebElement
    {
        private const string nullWebElement = "NullWebElement";
        public bool Displayed { get { return false; } }
        public bool Enabled { get { return false; } }
        public Point Location { get { return new Point(0, 0); } }
        public bool Selected { get { return false; } }
        public Size Size { get { return new Size(0, 0); } }
        public string TagName { get { return nullWebElement; } }
        public string Text { get { return nullWebElement; } }
        public void Clear() { }
        public void Click() { }
        public string GetAttribute(string attributeName) { return nullWebElement; }
        public string GetCssValue(string propertyName) { return nullWebElement; }
        public void SendKeys(string text) { }
        public void Submit() { }
        public IWebElement FindElement(By by) { return this; }
        public ReadOnlyCollection<IWebElement> FindElements(By by)
        {
            return new ReadOnlyCollection<IWebElement>(new List<IWebElement>());
        }
 
        private NullWebElement() { }
 
        private static NullWebElement instance;
        public static NullWebElement NULL
        {
            get
            {
                if (instance == null)
                {
                    instance = new NullWebElement();
                }
                return instance;
            }
        }
    

WebDriver facade

WebDriver facade main responsibility is to define custom behaviour on elements location. This gives you centralised control over elements location. Constructor takes browser type and uses the factory to create WebDriver instance which is used internally in the facade. FindElement method defines explicit wait. If element is not found then NullWebElement which is actually implementation of Null object pattern. The idea is to safely locate elements with try/catch and then just use them skipping checks for null.
public class WebDriverFacade
    {
        private IWebDriver webDriver = null;
        private TimeSpan waitForElement = TimeSpan.FromSeconds(5);
 
        public WebDriverFacade(Browsers browser)
        {
            WebDriverFactory factory = new WebDriverFactory();
            webDriver = factory.CreateInstance(browser);
        }
 
        public void Start(string url)
        {
            webDriver.Url = url;
            webDriver.Navigate();
        }
 
        public void Stop()
        {
            webDriver.Quit();
        }
 
        public object ExecuteJavaScript(string script)
        {
            return ((IJavaScriptExecutor)webDriver).
                ExecuteScript("return " + script);
        }
 
        public IWebElement FindElement(By by)
        {
            try
            {
                WebDriverWait wait = new WebDriverWait(webDriver, waitForElement);
                return wait.Until(ExpectedConditions.ElementIsVisible(by));
            }
            catch
            {
                return NullWebElement.NULL;
            }
        }

Tests

As I mention initially this post is about using efficiently design patterns in the framework automation project. Tests design are not being discussed here. Once you have designed the framework one simple test (without asserts) that makes search will look like code bellow.

Conclusion

Design patterns are used in enabling  to write maintainable code. They increase value of the developed code. As shown in this series of posts they are not so complicated and one can easily adopt them in  automation. Most important is design patterns increase r value as a professional. So make the time to learn them!
================================

Comments

Popular posts from this blog

How to calculate maximum number of concurrent users using littile law formula

How to develop a security test strategy

Singleton Design Pattern using Page Factory