Advanced Specification Design Pattern in Automated Testing

Advanced Specification Design Pattern in Automated Testing

In my last publication from the Design Patterns in Automated Testing Series, I explained to you how you can benefit from the usage of the Specification Design Pattern. In short your business rules can be recombined by chaining them together using boolean logic. The pattern increases the reusability, maintainability, readability and leads to loose coupling of the business rules from the business objects. In this article, I am going to extend the idea about it even more. I am going to show you how you can configure the rules through LINQ syntax, exchange the base specification class with extension methods. Further, you will learn how to decouple the specifications from the page objects through the usage of test context objects.

Simplify Basic Specification Design Pattern with Extension Methods

Basic Specification Design Pattern UML Class Diagram

classDiagram
    ISpecification~TEntity~ <|.. Specification~TEntity~
    Specification~TEntity~ <|-- CreditCardSpecification
    AndSpecification~TEntity~ --> Specification~TEntity~
    OrSpecification~TEntity~ --> Specification~TEntity~
    NotSpecification~TEntity~ --> Specification~TEntity~
    class ISpecification~TEntity~ {
        <<interface>>
        +bool IsSatisfiedBy(TEntity entity)
        +ISpecification~TEntity~ And(ISpecification~TEntity~ other)
        +ISpecification~TEntity~ Not()
        +ISpecification~TEntity~ Or(ISpecification~TEntity~ other)
    }
    class Specification~TEntity~ {
        +bool IsSatisfiedBy(TEntity entity)
        +ISpecification~TEntity~ And(ISpecification~TEntity~ other)
        +ISpecification~TEntity~ Not()
        +ISpecification~TEntity~ Or(ISpecification~TEntity~ other)
    }
    class AndSpecification~TEntity~ {
        +ISpecification~TEntity~ leftSpecification
        +ISpecification~TEntity~ rightSpecification
        +bool IsSatisfiedBy(TEntity entity)
    }
    class OrSpecification~TEntity~ {
        +ISpecification~TEntity~ leftSpecification
        +ISpecification~TEntity~ rightSpecification
        +bool IsSatisfiedBy(TEntity entity)
    }
    class NotSpecification~TEntity~ {
        +ISpecification~TEntity~ specification
        +bool IsSatisfiedBy(TEntity entity)
    }
    class CreditCardSpecification {
        +PurchaseTestInput purchaseTestInput
        +bool IsSatisfiedBy(PurchaseTestInput entity)
    }

Basic Specification Design Pattern Participants

The classes and objects participating in Specification Design Pattern are:

  • ISpecification

    Defines the interface for all specifications.

  • Specification

    The strategy that is responsible for applying and asserting promotional codes through the UI of the application.

  • AndSpecification

    Specification class used for chaining purposes defines the “And” boolean operator.

  • OrSpecification

    Defines the “Or” boolean operator.

  • NotSpecification

    Defines the “Not” boolean operator.

  • CreditCardSpecification

    A concrete specification where the IsSatisfiedBy method is implemented. Holds the concrete business rule.

Extension Methods’ Specification Design Pattern UML Class Diagram

classDiagram
    ISpecification~TEntity~ <|.. CreditCardSpecification
    ISpecification~TEntity~ <|.. AndSpecification~TEntity~
    ISpecification~TEntity~ <|.. OrSpecification~TEntity~
    ISpecification~TEntity~ <|.. NotSpecification~TEntity~
    SpecificationsExtensionMethods ..> ISpecification~TEntity~
    class ISpecification~TEntity~ {
        <<interface>>
        +bool IsSatisfiedBy(TEntity entity)
        +ISpecification~TEntity~ And(ISpecification~TEntity~ other)
        +ISpecification~TEntity~ Not()
        +ISpecification~TEntity~ Or(ISpecification~TEntity~ other)
    }
    class CreditCardSpecification {
        +PurchaseTestInput purchaseTestInput
        +bool IsSatisfiedBy(PurchaseTestInput entity)
    }
    class AndSpecification~TEntity~ {
        +ISpecification~TEntity~ leftSpecification
        +ISpecification~TEntity~ rightSpecification
        +bool IsSatisfiedBy(TEntity entity)
    }
    class OrSpecification~TEntity~ {
        +ISpecification~TEntity~ leftSpecification
        +ISpecification~TEntity~ rightSpecification
        +bool IsSatisfiedBy(TEntity entity)
    }
    class NotSpecification~TEntity~ {
        +ISpecification~TEntity~ specification
        +bool IsSatisfiedBy(TEntity entity)
    }
    class SpecificationsExtensionMethods {
        +And~TEntity~(ISpecification~TEntity~ left, ISpecification~TEntity~ right)
        +Not~TEntity~(ISpecification~TEntity~ specification)
        +Or~TEntity~(ISpecification~TEntity~ left, ISpecification~TEntity~ other)
    }

Extension Methods’ Specification Design Pattern Participants

The classes and objects participating in Specification Design Pattern are:

  • ISpecification

    Defines the interface for all specifications.

  • SpecificationExtensionMethods

    A static class that contains the implementation of the And, Or and Not methods as static extension methods.

  • AndSpecification

    Specification class used for chaining purposes defines the “And” boolean operator.

  • OrSpecification

    Defines the “Or” boolean operator.

  • NotSpecification

    Defines the “Not” boolean operator.

  • CreditCardSpecification

    A concrete specification where the IsSatisfiedBy method is implemented. Holds the concrete business rule.

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

Extension Methods’ Specification Design Pattern C# Code

The subtle difference between the basic Specification Design Pattern and the later is that the abstract Specification class is removed. The concrete specification classes implement the ISpecification directly. The logic of the abstract Specification base class is moved to extension methods in the static SpecificationsExtensionMethods class.

public static class SpecificationsExtensionMethods
{
    public static ISpecification<TEntity> And<TEntity>(this ISpecification<TEntity> leftSpecification, ISpecification<TEntity> rightSpecification)
    {
        return new AndSpecification<TEntity>(leftSpecification, rightSpecification);
    }

    public static ISpecification<TEntity> Or<TEntity>(this ISpecification<TEntity> leftSpecification, ISpecification<TEntity> rightSpecification)
    {
        return new OrSpecification<TEntity>(leftSpecification, rightSpecification);
    }

    public static ISpecification<TEntity> Not<TEntity>(this ISpecification<TEntity> specification)
    {
        return new NotSpecification<TEntity>(specification);
    }
}

It is not a big change but this way you simplify the inheritance chain. However, this is not the intended usage of the extension methods so you should carefully analyse the pros and cons of the approach.

LINQ Specification Design Pattern

LINQ Specification Design Pattern UML Class Diagram

classDiagram
    ISpecification~TEntity~ <|.. Specification~TEntity~
    Specification~TEntity~ <|-- ExpressionSpecification~TEntity~
    class ISpecification~TEntity~ {
        <<interface>>
        +bool IsSatisfiedBy(TEntity entity)
        +ISpecification~TEntity~ And(ISpecification~TEntity~ other)
        +ISpecification~TEntity~ Not()
        +ISpecification~TEntity~ Or(ISpecification~TEntity~ other)
    }
    class Specification~TEntity~ {
        +bool IsSatisfiedBy(TEntity entity)
        +ISpecification~TEntity~ And(ISpecification~TEntity~ other)
        +ISpecification~TEntity~ Not()
        +ISpecification~TEntity~ Or(ISpecification~TEntity~ other)
    }
    class ExpressionSpecification~TEntity~ {
        +Func~TEntity, bool~ expression
        +bool IsSatisfiedBy(TEntity entity)
    }

LINQ Specification Design Pattern Participants

The classes and objects participating in Specification Design Pattern are:

  • ISpecification

    Defines the interface for all specifications.

  • Specification

    An abstract class that contains the implementation of the And, Or and Not methods. Only the IsSatisfiedBy varies based on the business rule.

  • ExpressionSpecification

    The class eliminates the need for concrete specifications. It accepts a boolean function as a parameter which represents the business rule.

LINQ Specification Design Pattern C# Code

public class ExpressionSpecification<TEntity> : Specification<TEntity>
{
    private readonly Func<TEntity, bool> expression;

    public ExpressionSpecification(Func<TEntity, bool> expression)
    {
        if (expression == null)
        {
            throw new ArgumentNullException();
        }
        this.expression = expression;
    }

    public override bool IsSatisfiedBy(TEntity entity)
    {
        return this.expression(entity);
    }
}
public partial class PlaceOrderPage : BasePage
{
    private readonly PurchaseTestInput purchaseTestInput;
    private readonly ISpecification<PurchaseTestInput> promotionalPurchaseSpecification;
    private readonly ISpecification<PurchaseTestInput> creditCardSpecification;
    private readonly ISpecification<PurchaseTestInput> wiretransferSpecification;
    private readonly ISpecification<PurchaseTestInput> freePurchaseSpecification;

    public PlaceOrderPage(IWebDriver driver, PurchaseTestInput purchaseTestInput)
        : base(driver)
    {
        this.purchaseTestInput = purchaseTestInput;
        this.creditCardSpecification = new ExpressionSpecification<PurchaseTestInput>(x => !string.IsNullOrEmpty(x.CreditCardNumber));
        this.freePurchaseSpecification = new ExpressionSpecification<PurchaseTestInput>(x => x.TotalPrice == 0);
        this.wiretransferSpecification = new ExpressionSpecification<PurchaseTestInput>(x => x.IsWiretransfer);
        this.promotionalPurchaseSpecification = new ExpressionSpecification<PurchaseTestInput>(x => x.IsPromotionalPurchase && x.TotalPrice < 5);
    }

    public override string Url
    {
        get
        {
            return @"http://www.bing.com/";
        }
    }

    public void ChoosePaymentMethod()
    {
        if (this.creditCardSpecification.
        And(this.wiretransferSpecification.Not()).
        And(this.freePurchaseSpecification.Not()).
        And(this.promotionalPurchaseSpecification.Not()).
        IsSatisfiedBy(this.purchaseTestInput))
        {
            this.CreditCard.SendKeys("371449635398431");
            this.SecurityNumber.SendKeys("1234");
        }
        else
        {
            this.Wiretransfer.SendKeys("pathToFile");
        }
    }
}

Unfortunately, as the expression specifications’ variables are declared as private, we cannot reuse them in the page’s asserter. I am going to show you how to refactor the code to fix the mentioned issue.

Improve LINQ Specification Design Pattern with Test Context

To be able to reuse the expression specifications, their configurations and chaining, we need to create two new classes. OrderTestContextConfigurator is going to expose the specifications as public properties. It is going to be responsible for their setup. This way the page objects follow the Single Responsibility Principle more strictly as they won’t be responsible for this configuration anymore, they will provide only service methods.

public class OrderTestContextConfigurator
{
    public OrderTestContextConfigurator()
    {
        this.CreditCardSpecification = new ExpressionSpecification<PurchaseTestInput>(x => !string.IsNullOrEmpty(x.CreditCardNumber));
        this.FreePurchaseSpecification = new ExpressionSpecification<PurchaseTestInput>(x => x.TotalPrice == 0);
        this.WiretransferSpecification = new ExpressionSpecification<PurchaseTestInput>(x => x.IsWiretransfer);
        this.PromotionalPurchaseSpecification = new ExpressionSpecification<PurchaseTestInput>(x => x.IsPromotionalPurchase && x.TotalPrice < 5);
    }

    public ISpecification<PurchaseTestInput> PromotionalPurchaseSpecification { get; private set; }

    public ISpecification<PurchaseTestInput> CreditCardSpecification { get; private set; }

    public ISpecification<PurchaseTestInput> WiretransferSpecification { get; private set; }

    public ISpecification<PurchaseTestInput> FreePurchaseSpecification { get; private set; }
}
public class OrderTestContext
{
    public OrderTestContext(PurchaseTestInput purchaseTestInput, OrderTestContextConfigurator orderTestContextConfigurator)
    {
        this.PurchaseTestInput = purchaseTestInput;
        this.IsPromoCodePurchase = orderTestContextConfigurator.FreePurchaseSpecification.
            Or(orderTestContextConfigurator.PromotionalPurchaseSpecification).
            IsSatisfiedBy(purchaseTestInput);
        this.IsCreditCardPurchase =
            orderTestContextConfigurator.CreditCardSpecification.
            And(orderTestContextConfigurator.WiretransferSpecification.Not()).
            And(orderTestContextConfigurator.FreePurchaseSpecification.Not()).
            And(orderTestContextConfigurator.PromotionalPurchaseSpecification.Not()).
            IsSatisfiedBy(purchaseTestInput);
    }

    public PurchaseTestInput PurchaseTestInput { get; private set; }

    public bool IsPromoCodePurchase { get; private set; }

    public bool IsCreditCardPurchase { get; private set; }
}

Through its usage, you can reuse the business rules and their chaining logic. It is up to you if you will expose it as a public property of the page object or pass it always as a parameter.

Related Articles

Design Patterns

Page Objects- Elements Access Styles- 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- Elements Access Styles- WebDriver C#

Design Patterns

Advanced Null Object Design Pattern in Automated Testing

This is the second article dedicated to the Null Object Design Pattern part of the Design Patterns in Automated Testing series. In the last post, I showed you h

Advanced Null Object Design Pattern in Automated Testing

Design Patterns

Custom Test Automation Framework: Modularity Planning and Design

In the new Build Custom Automation Framework Series, we will look into detailed explanations on creating custom test automation frameworks. Many people starting

Custom Test Automation Framework: Modularity Planning and Design

Design Patterns

Null Object Design Pattern in Automated Testing

If you are a regular reader of Automate The Planet you have most probably read some of my articles about Design Patterns in Automated Testing. The newest articl

Null Object Design Pattern in Automated Testing

Design Patterns

Page Objects- Partial Classes Singleton Design Pattern- 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 Singleton Design Pattern- WebDriver C#

Design Patterns

Singleton Design Pattern in Automated Testing

Ensure a class has only one instance and provide a global point of access to it.Instance control– prevents other objects from instantiating their own copies of

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