Click here to Skip to main content
15,879,184 members
Articles / Programming Languages / C#

Use IoC Container to Create Page Object Pattern on Steroids

Rate me:
Please Sign up or sign in to vote.
4.43/5 (7 votes)
23 Sep 2015Ms-PL7 min read 13.6K   7  
Create automation framework based on Page Object Pattern. Detailed overview how to create singleton pages via IoC container.

Introduction

In my previous articles from the series “Design Patterns in Automation Testing“, I explained in details how to make your test automation framework better through the implementation of Page Objects, Facades, and Singletons. Here I am going to improve these patterns through mixing them with Unity IoC Container (Inversion of Control). It is going to change slightly the hierarchy of classes and remove some base classes and generic parameters. In the end, the reading of the code is going to be easier.

Image 1

IoC Container

Definition by Wikipedia

Quote:

In software engineering, inversion of control (IoC) describes a design in which custom-written portions of a computer program receive the flow of control from a generic, reusable library. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the reusable code that calls into the custom, or task-specific, code.

Inversion of control serves the following design purposes:

  • To decouple the execution of a task from implementation.
  • To focus a module on the designed-for task.
  • To free modules from assumptions about how other systems do what they do and instead rely on contracts.
  • To prevent side effects when replacing a module.

UML Class Diagram

Image 2

Participants

The classes and objects participating in this pattern are:

  • Page Object (WikipediaMainPage)- Holds the actions that can be performed on the page like Search and Navigate. It 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.
  • ICustomPage– Contains only the signatures of methods of an appropriate page object.
  • BasePage<S, M> – Gives access to the child’s page element map class and defines a standard navigation operation.
  • BasePage<M, V> – Adds an instance to the child page’s validator class through the Validate method.
  • BaseElementMap – Provides easier access to current browser and functions to switch between different frames.
  • BasePageValidator<M> – Gives all child validators instance to the current element map and the page object itself.

IoC Container and Page Objects C# Code

Test’s Test Case

The primary goal of the example is going to be to search for a term in Wikipedia and validate functionalities on the found article’s page.

1. Navigate to Wikipedia site

2. Search for Term

3. Validate Contents Hide toggle

4. Click Contents Hide link

5. Validate Contents list is not visible

Image 3

If we don’t use IoC Container, our test looks like the code below.

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

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

    [TestMethod]
    public void TestWikiContentsToggle()
    {
        WikipediaMainPage wikiPage = new WikipediaMainPage();
        wikiPage.Navigate();
        wikiPage.Search("Quality assurance");
        wikiPage.Validate().ToogleLinkTextHide();
        wikiPage.Validate().ContentsListVisible();
        wikiPage.ToggleContents();
        wikiPage.Validate().ToogleLinkTextShow();
        wikiPage.Validate().ContentsListHidden();
    }
}

The primary disadvantage is the need to create the WikipediaMainPage for every single test. As I discussed in my previous article devoted to Singleton Page Objects, the core framework classes can be modified to produce only a single instance of every page per test run. However, the examined approach in the post brings tremendous complication of the class hierarchy through multiple new generic parameters and types. Observe this short code example.

public abstract class BasePageSingletonDerived<S, M> : ThreadSafeNestedContructorsBaseSingleton<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();
    }
}

 It is possible to simplify the architecture of the framework through the usage of Unity IoC Container for the creation of the page objects. To be able to achieve it, a slight change of the main page object pattern’s classes is needed. In this example, I’m going to use the simplified version of the BasePages.

public class BasePage<M>
    where M : BasePageElementMap, new()
{
    protected readonly string url;

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

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

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

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

public class BasePage<M, V> : BasePage<M>
    where M : BasePageElementMap, new()
    where V : BasePageValidator<M>, new()
{
    public BasePage(string url) : base(url)
    {
    }

    public BasePage()
    {
    }

    public V Validate()
    {
        return new V();
    }
}

As you can see, the inheritance from singleton class is missing, and the same goes for the half of the generic parameters.

So that to be able to use the IoC Container in the tests the WikipediaMainPage should implement an interface.

C#
public class WikipediaMainPage : BasePage<WikipediaMainPageMap, WikipediaMainPageValidator>, IWikipediaMainPage
{
    public WikipediaMainPage()
        : base(@"<a href="https://en.wikipedia.org" rel="noreferrer" style="removed:help;display:inline !important;">https://en.wikipedia.org</a>")
    {
    }

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

    public void ToggleContents()
    {
        this.Map.ContentsToggleLink.Click();
    }
}

The only difference compared to the typical implementation of the Page Object Pattern is the IWikipediaMainPage interface.

C#
public interface IWikipediaMainPage
{
    void Navigate(string part = "");

    WikipediaMainPageValidator Validate();

    void Search(string textToType);

    void ToggleContents();
}

It should hold only the publicly available members of the page object, I want to emphasize that the Map property is not present here because it is a bad practice to use it directly in the tests.

IoC Container Page Objects in Tests

Image 4

In this example, I’m using the simplified registration process of the Unity IoC Container. In the later examples, I’m going to show you how to configure it through XML configuration and abstract the logic in a new PageFactory class.

C#
[TestClass]
public class UnityWikipediaTests
{
    private static IUnityContainer pageFactory = new UnityContainer();

    [AssemblyInitialize()]
    public static void MyTestInitialize(TestContext testContext)
    {
        pageFactory.RegisterType<IWikipediaMainPage, WikipediaMainPage>(new ContainerControlledLifetimeManager());
    }

    [TestInitialize]
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

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

    [TestMethod]
    public void TestWikiContentsToggle_Unity()
    {
        var wikiPage = pageFactory.Resolve<IWikipediaMainPage>();
        wikiPage.Navigate();
        wikiPage.Search("Quality assurance");
        wikiPage.Validate().ToogleLinkTextHide();
        wikiPage.Validate().ContentsListVisible();
        wikiPage.ToggleContents();
        wikiPage.Validate().ToogleLinkTextShow();
        wikiPage.Validate().ContentsListHidden();
    }
}

The AssemblyInitialize marks that the method is going to be executed only once for the current assembly which means that the registration process is going to be executed only once.

Through the following line:

C#
pageFactory.RegisterType<IWikipediaMainPage, WikipediaMainPage>(new ContainerControlledLifetimeManager());

We tell unity that we want to retrieve WikipediaMainPage when we resolve the interface IWikipediaMainPage. The parameter ContainerControlledLifetimeManager is used to point the IoC container to determine the object as a singleton. That means that if we resolve the interface a second time, the same instance is going to be returned.

Configure Unity IoC Container through XML Configuration

Image 5

First create a new XML file, named unity.config. Mark it to be always copied to the output folder.

XML
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="<a href="http://schemas.microsoft.com/practices/2010/unity" rel="noreferrer" style="removed:help;display:inline !important;">http://schemas.microsoft.com/practices/2010/unity</a>">
    <container>
      <register type="PatternsInAutomation.Tests.Advanced.Unity.WikipediaMainPage.IWikipediaMainPage, PatternsInAutomation.Tests"
                mapTo="PatternsInAutomation.Tests.Advanced.Unity.WikipediaMainPage.WikipediaMainPage, PatternsInAutomation.Tests">
        <lifetime type="singleton"/>
      </register>
    </container>
  </unity>
</configuration>

Under the unity section, paste the register parts where you need to point Unity how to map your interfaces. The format is as follows- type=”FullNamespace.InterfaceName, AssemblyName” mapTo =”FullNamespace.ClassName, AssemblyName”.

XML
<lifetime type="singleton"/>

The above line tells the IoC Container to create the specified class as Singleton.

So that the Unity container to be able to retrieve the desired configuration from the file, you need to configure the IoC container to work with it. One way to do it is to add the code to the AssemlyInitialize method.

private static IUnityContainer pageFactory = new UnityContainer();

[AssemblyInitialize()]
public static void MyTestInitialize(TestContext testContext)
{
    var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = "unity.config" };
    Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
    var unitySection = (UnityConfigurationSection)configuration.GetSection("unity");
    pageFactory.LoadConfiguration(unitySection);
}

If the tests use a configuration file, the below row can be removed.

C#
pageFactory.RegisterType<IWikipediaMainPage, WikipediaMainPage>(new ContainerControlledLifetimeManager());

Improve IoC Container Page Objects Further- PageFactory

If I should be honest when I had read for the first time the code that uses directly the Unity to Resolve the page objects, I didn’t like it so much. Because of that, I created a class, and its primary goal was to hide the IoC container’s work and its configuration.

C#
public static class PageFactory
{
    private static IUnityContainer container; 

    static PageFactory()
    {
        var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = "unity.config" };
        Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        var unitySection = (UnityConfigurationSection)configuration.GetSection("unity");
        container = new UnityContainer(); 
        container.LoadConfiguration(unitySection);
    }

    public static T Get<T>()
    {
        return container.Resolve<T>();
    }
}

Instead of configuring the Unity in the AssemblyInitialize method, the static constructor is going be called only once and so load all necessary settings.

Image 6

Usage of IoC Container PageFactory

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

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

    [TestMethod]
    public void TestWikiContentsToggle_Unity()
    {
        var wikiPage = PageFactory.Get<IWikipediaMainPage>();
        ////var wikiPage = pageFactory.Resolve<IWikipediaMainPage>();
        wikiPage.Navigate();
        wikiPage.Search("Quality assurance");
        wikiPage.Validate().ToogleLinkTextHide();
        wikiPage.Validate().ContentsListVisible();
        wikiPage.ToggleContents();
        wikiPage.Validate().ToogleLinkTextShow();
        wikiPage.Validate().ContentsListHidden();
    }
}

This way the code is much simpler and easy to read.

“Improve” Page Interfaces trough Inheritance

In the previously mentioned IWikipediaMainPage interface, there were present the Navigate and Validate methods that derived from the BasePage classes. Which means that they should be present for every page’s interface too. The same way there are base pages to hold these two standard methods, there could be a base interface.

C#
public interface IPage<M, V>
    where M : BasePageElementMap, new()
    where V : BasePageValidator<M>, new()
{
    V Validate();

    void Navigate(string part = "");
}

The refactor version of the IWikipediaMainPage interface is going to look as follows:

C#
public interface IWikipediaMainPage<M, V> : IPage<M, V>
    where M : BasePageElementMap, new()
    where V : BasePageValidator<M>, new() 
{
    void Search(string textToType);

    void ToggleContents();
}

Personally I believe that it is better to add these two methods every time instead of complicating the code so much. However, you have always the choice to do it.

Pros and Cons of IoC Container Page Objects Compared to Singleton Page Objects

Cons

Dependency to Unity IoC Container.

Harder to use. Every time you need to resolve the interface to get the page.

Add code through the interfaces.

An additional configuration is required by code or XML.

Image 7

Pros

+ Simplified hierarchy of classes.

+ Possibility to change the lifetime scope of the page objects.

+ More abstract tests- option to change the page object classes if needed.

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:

 

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

The post Use IoC Container to Create Page Object Pattern on Steroids appeared first on Automate The Planet.

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

 
-- There are no messages in this forum --