Advanced Behaviours Design Pattern in Automated Testing Part 1

Advanced Behaviours Design Pattern in Automated Testing Part 1

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

classDiagram
    BehaviourExecutor ..> IBehaviour
    IBehaviour <|.. AssertBehaviour
    IBehaviour <|.. ActionBehaviour
    ActionBehaviour <|-- WaitableActionBehaviour
    WaitableActionBehaviour <|-- WaitableAssertableActionBehaviour
    ActionBehaviour <|-- ItemPageNavigationBehaviour
    WaitableActionBehaviour <|-- SignInPageLoginBehaviour
    ItemPageNavigationBehaviour ..> UnityContainerFactory
    SignInPageLoginBehaviour ..> UnityContainerFactory
    SignInPageLoginBehaviour ..> SignInPage
    SignInPageLoginBehaviour ..> ShippingAddressPage
    ItemPageNavigationBehaviour ..> ItemPage
    class IBehaviour {
        <<interface>>
        +Execute()
    }
    class BehaviourExecutor {
        +Execute()
    }
    class AssertBehaviour {
        +Assert()
        +Execute()
    }
    class ActionBehaviour {
        +Execute()
        +PerformAct()
    }
    class WaitableActionBehaviour {
        +Execute()
        +PerformAct()
        +PerformPostActWait()
    }
    class WaitableAssertableActionBehaviour {
        +Execute()
        +PerformAct()
        +PerformPostActAssert()
        +PerformPostActWaitAssert()
        +PerformPreActAssert()
        +PerformPreActWait()
    }
    class ItemPageNavigationBehaviour {
        +itemUrl
        +PerformAct()
    }
    class SignInPageLoginBehaviour {
        +ClientLoginInfo
        +PerformAct()
        +PerformPostActWait()
    }
    class UnityContainerFactory {
        +GetContainer()
    }
    class ItemPage {
        +Navigate()
    }
    class SignInPage {
        +Login()
    }
    class ShippingAddressPage {
        +WaitForPageToLoad()
    }

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.

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

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


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.

Related Articles

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

Proxy Design Pattern in Automated Testing

Achieving high-quality test automation that brings value- you need to understand core programming concepts such as SOLID and the usage of design patterns. In th

Proxy Design Pattern in Automated Testing

Design Patterns

Page Objects- Partial Classes Base Pages- 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 Base Pages- WebDriver C#

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

Advanced Strategy Design Pattern in Automated Testing

4. Fill Shipping Info

Advanced Strategy Design Pattern in Automated Testing

Design Patterns

Specification Design Pattern in Automated Testing

If you follow my series about Design Patterns in Automated Testing, I explain how you can utilize the power of various design patterns in your tests. In the cur

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