Use IoC Container to Create Page Object Pattern on Steroids

Use IoC Container to Create Page Object Pattern on Steroids

In my previous articles from the series “Design Patterns in Automated 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). This 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.

IoC Container

Definition

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.

  • To decouple the execution of a task from implementation.
  • To focus a module on the task it is designed for.
  • 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

classDiagram
    ICustomPage <|.. WikipediaMainPage
    BasePage <|-- WikipediaMainPage
    BasePage o-- BaseElementMap
    BasePage o-- BasePageValidator
    class ICustomPage {
        <<interface>>
        +Navigate()
        +Search(string text)
    }
    class WikipediaMainPage {
        +Navigate()
        +Search(string text)
        +ToggleContents()
        +Validate()
    }
    class BasePage {
        +Map
        +Validate()
        +Navigate(string url)
    }
    class BaseElementMap {
        +IWebDriver Driver
    }
    class BasePageValidator {
        +Map
    }

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 a particular page object. BasePage - Gives access to the child’s page element map class and defines a standard navigation operation.

  • BasePage

    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

    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

IoC Container Test Case Wikipedia

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


public class UnityWikipediaTests
{
    
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

    
    public void TeardownTest()
    {
        Driver.StopBrowser();
    }

    
    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 classes. 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();
    }
}

To simplify the architecture of the framework, it is possible to use Unity IoC Container for the creation of the page objects. In order 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();
    }
}
public class WikipediaMainPage : BasePage<WikipediaMainPageMap, WikipediaMainPageValidator>, IWikipediaMainPage
{
    public WikipediaMainPage()
        : base(@"mySiteUrl")
    {
    }

    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.

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.

For 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:

Defining High-Quality Test Attributes for Automated Tests

Benchmarking for Assessing Automated Test Components Performance

Generic Repository Design Pattern- Test Data Preparation

IoC Container Page Objects in Tests

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.


public class UnityWikipediaTests
{
    private static IUnityContainer pageFactory = new UnityContainer();

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

    
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

    
    public void TeardownTest()
    {
        Driver.StopBrowser();
    }

    
    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();
    }
}
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

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

<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <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”.

<lifetime type="singleton"/>
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.

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.

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.

Usage of IoC Container PageFactory


public class UnityWikipediaTests
{
    
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

    
    public void TeardownTest()
    {
        Driver.StopBrowser();
    }

    
    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 through Inheritance

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

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:

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

Pros and Cons

  • 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.
  • 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.

Related Articles

Design Patterns

Rules Design Pattern in Automated Testing

Separate the logic of each individual rule and its effects into its own class. Separate the selection and processing of rules into a separate Evaluator class.

Rules Design Pattern in Automated Testing

Design Patterns

Facade Design Pattern in Automated Testing

An object that provides a simplified interface to a larger body of code, such as class library. Make a software library easier to use, understand and more reada

Facade Design Pattern in Automated Testing

Design Patterns

Generic Repository Design Pattern- Test Data Preparation

Often we can run the tests against an empty DB. However, we still need initial data. We can generate it ourselves. To do so, we need to add a code for accessing

Generic Repository Design Pattern- Test Data Preparation

Design Patterns

Page Objects- Partial Classes Fluent API- WebDriver C#

Editorial Note: I originally wrote this post for the Test Huddle Blog. You can check out the original here, at their site.

Page Objects- Partial Classes Fluent API- WebDriver C#

Design Patterns

Strategy Design Pattern in Automated Testing

In my previous articles from the series "Design Patterns in Automated Testing", I explained in details how to make your test automation framework better through

Strategy Design Pattern in Automated Testing

Design Patterns

Advanced Strategy Design Pattern in Automated Testing

4. Fill Shipping Info

Advanced Strategy Design Pattern in Automated Testing
Anton Angelov

About the author

Anton Angelov is Managing Director, Co-Founder, and Chief Test Automation Architect at Automate The Planet — a boutique consulting firm specializing in AI-augmented test automation strategy, implementation, and enablement. He is the creator of BELLATRIX, a cross-platform framework for web, mobile, desktop, and API testing, and the author of 8 bestselling books on test automation. A speaker at 60+ international conferences and researcher in AI-driven testing and LLM-based automation, he has been recognized as QA of the Decade and Webit Changemaker 2025.