Click here to Skip to main content
15,867,141 members
Articles / Programming Languages / C#

Singleton Design Pattern in Automation Testing

Rate me:
Please Sign up or sign in to vote.
4.95/5 (14 votes)
24 Sep 2015Ms-PL5 min read 28.1K   23   8
Explains in detail how to implement Singleton Design Pattern. Practical examples how to use it in automation tests, following all SOLID principles.

Introduction

In my series of articles “Design Patterns in Automation Testing“, I am presenting you the most useful techniques for structuring the code of the automation tests. Today’s publication is about the Singleton Design Pattern. In my previous posts, I have explained how to use the Page Object and Facade Design Patterns. If you are not familiar with them, I advise you to read my articles on the matter. The Singleton Design Pattern can be used in the automation tests to build up easier access to page objects and facades. Its usage can speed up the tests writing process dramatically. In this article, I am going to show you the best and most straightforward approaches to integrating the Singleton Design Pattern in your test automation framework.

Image 1

Definition

Ensure a class has only one instance and provide a global point of access to it.

  • Instance control – prevents other objects from instantiating their copies of the Singleton object, ensuring that all objects access the single instance.
  • Flexibility – since the class controls the instantiation process, the class has the flexibility to change the instantiation process.
  • Lazy initialization – defers the object’s creation until it is first used.

UML Class Diagram

Image 2

Participants

The classes and objects participating in this pattern are:

  • Page Objects (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.
  • BasePage<S, M> – Gives access to the child’s page element map class and defines a standard navigation operation.
  • BasePage<S, M, V> – Adds an instance to the child page’s validator class through the Validate method.
  • BaseSingleton – This is an abstract class that contains a static property of its child instance

Singleton Design Pattern C# Code

Create Singleton in BasePage

The most straightforward way to integrate the Singleton Design Pattern in all of your existing page objects is to add a static variable and property in the BasePage class. If you are not familiar what the BasePage classes are, refer to my article- Advanced Page Object Pattern in Automation Testing.

C#
public class BasePage<M>
    where M : BasePageElementMap, new()
{
    private static BasePage<M> instance;

    protected readonly string url;

    public BasePage(string url)
    {
        this.url = url;
    }

    public BasePage()
    {
        this.url = null;
    }

    public static BasePage<M> Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new BasePage<M>();
            }

            return instance;
        }
    }

    protected M Map
    {
        get
        {
            return new M();
        }
    }

    public virtual void Navigate(string part = "")
    {
        Driver.Browser.Navigate().GoToUrl(string.Concat(url, part));
    }
}

The drawback of the above solution is that you cannot reuse the singleton implementation for your Facade classes. The answer to the mentioned problem is to create a base generic singleton class that can be derived from all pages and facades.

Image 3

Non-thread-safe BaseSingleton Class

Find below, the most trivial implementation of the generic base class for the Singleton Design Pattern.

C#
public abstract class Nonthread-safeBaseSingleton<T>
    where T: new()
{
    private static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new T();
            }

            return instance;
        }
    }
}

In most scenarios for automation tests, this solution should be sufficient. However, this implementation is not thread-safe. To be possible to execute tests in parallel, I am going to propose you other variations of the base class that are thread-safe.

Image 4

Thread-safe BaseSingleton Class with Lock

C#
public abstract class Thread-safeBaseSingleton<T>
    where T : new()
{
    private static T instance;
    private static readonly object lockObject = new object();

    private thread-safeBaseSingleton()
    {
    }

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new T();
                    }
                }
            }
            return instance;
        }
    }
}

Here, I am using a lock statement that ensures that one thread does not enter a critical section of code while another thread is in the critical section. If another thread tries to enter a locked code, it will wait, block until the object is released. As you can see, the code doesn’t lock the T instance. Instead, it locks its internal object. It is done to prevent deadlocks.

Thread-safe BaseSingleton Class with Lazy

The built-in .NET framework generic class Lazy<T> saves some code for implementing the lazy initialization. Also, it is thread-safe.

C#
public abstract class ThreadSafeLazyBaseSingleton<T>
    where T : new()
{
    private static readonly Lazy<T> lazy = new Lazy<T>(() => new T());
    
    public static T Instance
    {
        get
        {
            return lazy.Value;
        }
    }
}

Thread-safe BaseSingleton Class with Nested Classes

The most complicated variation of the base class implementing Singleton Design Pattern uses nested classes and reflection.

C#
public abstract class ThreadSafeNestedContructorsBaseSingleton<T>
{
    public static T Instance
    {
        get
        {
            return SingletonFactory.Instance;
        }
    }

    internal static class SingletonFactory
    {
        internal static T Instance;

        static SingletonFactory()
        {
            CreateInstance(typeof(T));
        }

        public static T CreateInstance(Type type)
        {
            ConstructorInfo[] ctorsPublic = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public);

            if (ctorsPublic.Length > 0)
            {
                throw new Exception(string.Concat(type.FullName, 
                " has one or more public constructors so the property cannot be enforced."));
            }
                    

            ConstructorInfo nonPublicConstructor =
                type.GetConstructor(BindingFlags.Instance | 
                BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);

            if (nonPublicConstructor == null)
            {
                throw new Exception(string.Concat(type.FullName, 
                " does not have a private/protected constructor so the property cannot be enforced."));
            }

            try
            {
                return Instance = (T)nonPublicConstructor.Invoke(new object[0]);
            }
            catch (Exception e)
            {
                throw new Exception(
                    string.Concat("The Singleton could not be constructed. 
                    Check if ", type.FullName, " has a default constructor."), e);
            }
        }
    }
}

The nested class contains static constructor to tell the C# compiler not to mark the type as before-field-init. Now the BasePage classes should not have constructors to follow the Singleton Design Pattern, because of that the new() class constraint is removed.

Image 5

The structure of the base classes for the Page Object Pattern should be slightly changed due to the earlier mentioned reason.

C#
public abstract class BasePageSingletonDerived<S, M> : thread-safeNestedContructorsBaseSingleton<S>
    where M : BasePageElementMap, new()
    where S : BasePageSingletonDerived<S, M>
{
    protected M Map
    {
        get
        {
            return new M();
        }
    }

    public virtual void Navigate(string url = "")
    {
        Driver.Browser.Navigate().GoToUrl(string.Concat(url));
    }
}

public abstract class BasePageSingletonDerived<S, M, V> : BasePageSingletonDerived<S, M>
    where M : BasePageElementMap, new()
    where V : BasePageValidator<M>, new()
    where S : BasePageSingletonDerived<S, M, V>
{
    public V Validate()
    {
        return new V();
    }
}

One of the significant changes in the code is the new S generic parameter that represents the type of the child page which is going to be initialized. Also, you can notice the absence of constructors and that the both classes are marked as abstract.

The page object class that derives from these classes can look like the following:

C#
public class BingMainPage : BasePageSingletonDerived<BingMainPage, 
	BingMainPageElementMap, BingMainPageValidator>
{
    private BingMainPage() { }

    public void Search(string textToType)
    {
        this.Map.SearchBox.Clear();
        this.Map.SearchBox.SendKeys(textToType);
        this.Map.GoButton.Click();
    }

    public override void Navigate(string url = "<a href="http://www.bing.com/" rel="noreferrer" style="removed:help;display:inline !important;">http://www.bing.com/</a>")
    {
        base.Navigate(url);
    }
}

I want to emphasize that the constructor of this class is marked as private, which prevents the creation of a default constructor.

Tests Using Singleton Design Pattern

Now you can use the page objects directly in your code without the keyword new, thanks to the singleton design pattern.

C#
[TestClass]
public class AdvancedBingSingletonTests
{       
    [TestInitialize]
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

    [TestCleanup]
    public void TeardownTest()
    {
        Driver.StopBrowser();
    }

    [TestMethod]
    public void SearchTextInBing_Advanced_PageObjectPattern_NoSingleton()
    {
        // Usage without Singleton
        P.BingMainPage bingMainPage = new P.BingMainPage();
        bingMainPage.Navigate();
        bingMainPage.Search("Automate The Planet");
        bingMainPage.Validate().ResultsCount(",000 RESULTS");
    }

    [TestMethod]
    public void SearchTextInBing_Advanced_PageObjectPattern_Singleton()
    {
        S.BingMainPage.Instance.Navigate();
        S.BingMainPage.Instance.Search("Automate The Planet");
        S.BingMainPage.Instance.Validate().ResultsCount(",000 RESULTS");
    }
}

So Far in the "Design Patterns in Automated Testing" Series

  1. Page Object Pattern
  2. Advanced Page Object Pattern
  3. Facade Design Pattern
  4. Singleton Design Pattern
  5. Fluent Page Object Pattern
  6. IoC Container and Page Objects
  7. Strategy Design Pattern
  8. Advanced Strategy Design Pattern
  9. Observer Design Pattern
  10. Observer Design Pattern via Events and Delegates
  11. Observer Design Pattern via IObservable and IObserver
  12. Decorator Design Pattern- Mixing Strategies
  13. Page Objects That Make Code More Maintainable
  14. Improved Facade Design Pattern in Automation Testing v.2.0
  15. Rules Design Pattern
  16. Specification Design Pattern
  17. Advanced Specification Design Pattern

 

If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!

Source Code

References

 

The post- Singleton Design Pattern in Automation Testing appeared first on Automate The Planet.

All images are purchased from DepositPhotos.com and cannot be downloaded and used for free.

License Agreement

This article was originally posted at http://automatetheplanet.com/singleton-design-pattern

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
CEO Automate The Planet
Bulgaria Bulgaria
CTO and Co-founder of Automate The Planet Ltd, inventor of BELLATRIX Test Automation Framework, author of "Design Patterns for High-Quality Automated Tests: High-Quality Test Attributes and Best Practices" in C# and Java. Nowadays, he leads a team of passionate engineers helping companies succeed with their test automation. Additionally, he consults companies and leads automated testing trainings, writes books, and gives conference talks. You can find him on LinkedIn every day.

Comments and Discussions

 
QuestionSingletons and Unit Tests are not the best friends Pin
HumanOsc24-Sep-15 21:09
HumanOsc24-Sep-15 21:09 
AnswerRe: Singletons and Unit Tests are not the best friends Pin
Anton Angelov24-Sep-15 21:24
Anton Angelov24-Sep-15 21:24 
GeneralRe: Singletons and Unit Tests are not the best friends Pin
rhyous25-Sep-15 19:14
rhyous25-Sep-15 19:14 
GeneralRe: Singletons and Unit Tests are not the best friends Pin
Anton Angelov4-Oct-15 11:10
Anton Angelov4-Oct-15 11:10 
QuestionHave you consider to post this as a tip? Pin
Nelek18-Aug-15 0:27
protectorNelek18-Aug-15 0:27 
Questionsingleton pattern implementation Pin
Lorenzo A.11-Aug-15 22:42
Lorenzo A.11-Aug-15 22:42 
AnswerRe: singleton pattern implementation Pin
rhyous25-Sep-15 19:17
rhyous25-Sep-15 19:17 
Questionthanks for sharing Pin
OwenGrad11-Aug-15 19:47
OwenGrad11-Aug-15 19:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.