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.
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
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
If we don’t use IoC Container, our test looks like the code below.
[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.
public class WikipediaMainPage : BasePage<WikipediaMainPageMap, WikipediaMainPageValidator>, IWikipediaMainPage
{
public WikipediaMainPage()
: base(@"<a href="https:
{
}
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.
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.
[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:
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="<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”.
<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.
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
[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>();
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.
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
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.
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
- Page Object Pattern
- Advanced Page Object Pattern
- Facade Design Pattern
- Singleton Design Pattern
- Fluent Page Object Pattern
- IoC Container and Page Objects
- Strategy Design Pattern
- Advanced Strategy Design Pattern
- Observer Design Pattern
- Observer Design Pattern via Events and Delegates
- Observer Design Pattern via IObservable and IObserver
- Decorator Design Pattern- Mixing Strategies
- Page Objects That Make Code More Maintainable
- Improved Facade Design Pattern in Automation Testing v.2.0
- Rules Design Pattern
- Specification Design Pattern
- Advanced Specification Design Pattern
If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!
Source Code
References:
CodeProject
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.