In my previous article dedicated to Behaviours Design Pattern, I shared with you how you can use the pattern to build system tests like a LEGO. The new article from the Design Patterns in Automated Testing Series is going to explain how you can improve even further your behaviours driven tests.
UML Class Diagram
Participants
The classes and objects participating in the improved Behaviours Design Pattern are:
- IBehaviour- Defines the interfaces for all behaviours. Contains only a single Execute method.
- AssertBehaviour- The base class for all assert-only behaviours. Contains two methods- Assert and Execute.
- ActionBehaviour- The base class for all actions-only behaviours. Contains only two methods PerformAct and Execute.
- WaitableActionBehaviour- Base class for all more complex behaviours that can contain simultaneously actions, asserts and wait logic.
- UnityContainerFactory- A class that creates and holds a single global instance to a Unity IoC container.
- ItemPageNavigationBehaviour- A concrete behaviour for the ItemPage class. It holds a logic for navigation. Its is an action behaviour.
- ItemPage- A concrete page object that provides different service operations that can be performed on the page. It is used in the specific behaviours.
- BehaviourExecutor- It is the class that executes the list of behaviours' workflows.
What Are the Problems That We Try to Solve?
In the previous examples, there was one major problem and that was the inability to pass properly the parameters to the behaviours. They all depended on a static test context class. As you probably know, the usage of static is not a best practice. The another issue that I saw was that the workflow's definition in the ExecutionEngine was somehow not flexible. If you need to add a new step you need to apply the change in the primary class that almost all tests depend on. I wanted to improve the flexibility of the tests.
Behaviours Design Pattern C# Code
One of the main differences compared to the previous examples is that the IBehaviour interface contains only a single method- Execute.
IBehaviour Interface Changes
public interface IBehaviour
{
void Execute();
}
Another major difference is that there are multiple base behaviours classes based on the use cases they need to solve in this variation of the behaviours design pattern. There is an actions-only and asserts-only classes. I believe that this leads to better OOP design instead of overriding only a part of the provided methods.
Actions-only Base Class
public abstract class ActionBehaviour : IBehaviour
{
public void Execute()
{
this.PerformAct();
}
protected abstract void PerformAct();
}
Assert-only Base Class
public abstract class AssertBehaviour : IBehaviour
{
public void Execute()
{
this.Assert();
}
protected abstract void Assert();
}
Waitable Action Base Class
public abstract class WaitableActionBehaviour : IBehaviour
{
public void Execute()
{
this.PerformAct();
this.PerformPostActWait();
}
protected abstract void PerformAct();
protected abstract void PerformPostActWait();
}
The class is useful when you need to perform an action and then wait for something, for example, wait for the page to load or for an element to become visible.
Waitable Assertable Action Base Class
public class WaitableAssertableActionBehaviour : IBehaviour
{
public void Execute()
{
this.PerformPreActWait();
this.PerformPreActAssert();
this.PerformAct();
this.PerformPostActAssert();
this.PerformPostActWait();
this.PerformPostActWaitAssert();
}
protected virtual void PerformPreActWait()
{
}
protected virtual void PerformPreActAssert()
{
}
protected virtual void PerformAct()
{
}
protected virtual void PerformPostActAssert()
{
}
protected virtual void PerformPostActWait()
{
}
protected virtual void PerformPostActWaitAssert()
{
}
}
The class is almost identical to the ones from the previous examples because it is too complex and contains all possible workflow steps in it. It is recommended to use it only if you cannot use some of the other available base behaviours classes.
Item Page Navigation Behaviour Refactored
Now the behaviour accepts the required itemUrl as a constructor's parameter. The dependent ItemPage is resolved not in the BehavioursExecutor but rather in the behaviour itself through the help of the UnityContainerFactory class that provides the current instance of the Unity IoC container. The behaviour needs only to navigate to a single page because of that it implements the simple ActionBehaviour base class.
public class ItemPageNavigationBehaviour : ActionBehaviour
{
private readonly ItemPage itemPage;
private readonly string itemUrl;
public ItemPageNavigationBehaviour(string itemUrl)
{
this.itemPage = UnityContainerFactory.GetContainer().Resolve<ItemPage>();
this.itemUrl = itemUrl;
}
protected override void PerformAct()
{
this.itemPage.Navigate(this.itemUrl);
}
}
UnityContainerFactory
It is a static class that contains a single static method GetContainer that returns the current instance of the Unity IoC Container. It can be also implemented as a singleton.
public static class UnityContainerFactory
{
private static IUnityContainer unityContainer;
static UnityContainerFactory()
{
unityContainer = new UnityContainer();
}
public static IUnityContainer GetContainer()
{
return unityContainer;
}
}
SignIn Page Login Behaviour Refactored
The changes are almost identical to the ones applied to the ItemPageNavigationBehaviour except the class inherits WaitableActionBehaviour. It overrides the PerformPostActWait method where it waits for the shipping address page to load.
public class SignInPageLoginBehaviour : WaitableActionBehaviour
{
private readonly SignInPage signInPage;
private readonly ShippingAddressPage shippingAddressPage;
private readonly ClientLoginInfo clientLoginInfo;
public SignInPageLoginBehaviour(ClientLoginInfo clientLoginInfo)
{
this.signInPage =
UnityContainerFactory.GetContainer().Resolve<SignInPage>();
this.shippingAddressPage =
UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
this.clientLoginInfo = clientLoginInfo;
}
protected override void PerformPostActWait()
{
this.shippingAddressPage.WaitForPageToLoad();
}
protected override void PerformAct()
{
this.signInPage.Login(this.clientLoginInfo.Email, this.clientLoginInfo.Password);
}
}
Simplified BehaviourExecutor
The executor now doesn't hold any complex logic, it contains only a single Execute method that accepts an array of behaviours' workflows.
public static class BehaviourExecutor
{
public static void Execute(params IBehaviour[] behaviours)
{
foreach (var behaviour in behaviours)
{
behaviour.Execute();
}
}
}
Behaviours Design Pattern in Tests
[TestMethod]
public void Purchase_SimpleBehaviourEngine()
{
var itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
var itemPrice = "40.49";
var clientPurchaseInfo = new ClientPurchaseInfo(
new ClientAddressInfo()
{
FullName = "John Smith",
Country = "United States",
Address1 = "950 Avenue of the Americas",
State = "New York",
City = "New York City",
Zip = "10001-2121",
Phone = "00164644885569"
});
clientPurchaseInfo.CouponCode = "99PERDIS";
var clientLoginInfo = new ClientLoginInfo()
{
Email = "g3984159@trbvm.com",
Password = "ASDFG_12345"
};
BehaviourExecutor.Execute(
new ItemPageNavigationBehaviour(itemUrl),
new ItemPageBuyBehaviour(),
new PreviewShoppingCartPageProceedBehaviour(),
new SignInPageLoginBehaviour(clientLoginInfo),
new ShippingAddressPageFillShippingBehaviour(clientPurchaseInfo),
new ShippingAddressPageFillDifferentBillingBehaviour(clientPurchaseInfo),
new ShippingAddressPageContinueBehaviour(),
new ShippingPaymentPageContinueBehaviour(),
new PlaceOrderPageAssertFinalAmountsBehaviour(itemPrice));
}
Compared to previous variations, you can notice few significant changes. First, you need to pass the behaviours through the new operator and pass any required parameters. Previously, you passed the behaviours via the typeof operator. You are no more obligated to initialise the static test context. I believe these changes made the tests much more flexible, readable and maintainable.
Design Patterns in Automated Testing
The post Advanced Behaviours Design Pattern in Automated Testing Part 1 appeared first on Automate The Planet.
All images are purchased from DepositPhotos.com and cannot be downloaded and used for free.
License Agreement