Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Advanced Behaviors Design Pattern in Automated Testing (Part 1)

DZone's Guide to

Advanced Behaviors Design Pattern in Automated Testing (Part 1)

We all know and love our design patterns. See how you can use them to make testing easier, too.

· DevOps Zone
Free Resource

Download “The DevOps Journey - From Waterfall to Continuous Delivery” to learn learn about the importance of integrating automated testing into the DevOps workflow, brought to you in partnership with Sauce Labs.

In my previous article dedicated to Behaviors 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 behavior-driven tests.

UML Class Diagram

Advanced Behaviours Design Pattern Class Diagram

Participants

The classes and objects participating in the improved Behaviors Design Pattern are:

  • IBehavior — Defines the interfaces for all behaviors. Contains only a single Execute method.
  • AssertBehavior — The base class for all assert-only behaviors. Contains two methods- Assert and Execute. 
  • ActionBehavior — The base class for all actions-only behaviors. Contains only two methods PerformAct and Execute. 
  • WaitableActionBehavior — Base class for all more complex behaviors 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.
  • ItemPageNavigationBehavior — A concrete behavior for the ItemPage class. It holds a logic for navigation. Its is an action behavior.
  • ItemPage — A concrete page object that provides different service operations that can be performed on the page. It is used in the specific behaviors.
  • BehaviorExecutor — It is the class that executes the list of behaviors' 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 behaviors. 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.

Behaviors Design Pattern C# Code

One of the main differences compared to the previous examples is that the IBehavior interface contains only a single method: Execute.

IBehavior Interface Changes

public interface IBehavior
{
 void Execute();
}

Another major difference is that there are multiple base behaviors classes based on the use cases they need to solve in this variation of the behaviors 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 ActionBehavior : IBehavior
{
 public void Execute()
 {
 this.PerformAct();
 }
 protected abstract void PerformAct();
}

Assert-only Base Class

public abstract class AssertBehavior : IBehavior
{
 public void Execute()
 {
 this.Assert();
 }
 protected abstract void Assert();
}

Waitable Action Base Class

public abstract class WaitableActionBehavior : IBehavior
{
 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, waiting for the page to load or for an element to become visible.

Waitable Assertable Action Base Class

public class WaitableAssertableActionBehavior : IBehavior
{
 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 behavior classes.

Item Page Navigation Behavior Refactored

Now the behavior accepts the required itemUrl as a constructor's parameter. The dependent ItemPage is resolved not in the BehaviorsExecutor but rather in the behavior itself through the help of the UnityContainerFactory class that provides the current instance of the Unity IoC container. The behavior needs only to navigate to a single page because of that it implements the simple ActionBehavior base class.

public class ItemPageNavigationBehavior : ActionBehavior
{
 private readonly ItemPage itemPage;
 private readonly string itemUrl;
 public ItemPageNavigationBehavior(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 Behavior Refactored

The changes are almost identical to the ones applied to the ItemPageNavigationBehavior except the class inherits WaitableActionBehavior. It overrides the PerformPostActWait method where it waits for the shipping address page to load.

public class SignInPageLoginBehavior : WaitableActionBehavior
{
 private readonly SignInPage signInPage;
 private readonly ShippingAddressPage shippingAddressPage;
 private readonly ClientLoginInfo clientLoginInfo;
 public SignInPageLoginBehavior(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 BehaviorExecutor

The executor now doesn't hold any complex logic, it contains only a single Execute method that accepts an array of behaviors' workflows.

public static class BehaviorExecutor
{
 public static void Execute(params IBehavior[] behaviors)
 {
 foreach (var behavior in behaviors)
 {
 behavior.Execute();
 }
 }
}

Behaviors Design Pattern in Tests

[TestMethod]
public void Purchase_SimpleBehaviorEngine()
{
 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"
 };
 BehaviorExecutor.Execute(
 new ItemPageNavigationBehavior(itemUrl),
 new ItemPageBuyBehavior(),
 new PreviewShoppingCartPageProceedBehavior(),
 new SignInPageLoginBehavior(clientLoginInfo),
 new ShippingAddressPageFillShippingBehavior(clientPurchaseInfo),
 new ShippingAddressPageFillDifferentBillingBehavior(clientPurchaseInfo),
 new ShippingAddressPageContinueBehavior(),
 new ShippingPaymentPageContinueBehavior(),
 new PlaceOrderPageAssertFinalAmountsBehavior(itemPrice));
}

Compared to previous variations, you can notice few significant changes. First, you need to pass the behaviors through the new operator and pass any required parameters. Previously, you passed the behaviors 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.

Discover how to optimize your DevOps workflows with our cloud-based automated testing infrastructure, brought to you in partnership with Sauce Labs

Topics:
automated testing

Published at DZone with permission of Anton Angelov, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}