Click here to Skip to main content
13,900,411 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

6.8K views
5 bookmarked
Posted 29 Jan 2016
Licenced Ms-PL

Advanced Specification Design Pattern in Automated Testing

, 29 Jan 2016
Rate this:
Please Sign up or sign in to vote.
A detailed overview how to utilize the Specification Design Pattern in automated tests to segregate business rules based on Single responsibility principle.

Introduction

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.

Advanced Specification Design Pattern

Simplify Basic Specification Design Pattern with Extension Methods

Basic Specification Design Pattern UML Class Diagram

Specification Design Pattern Class Diagram

Basic 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.
  • 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

Extension Methods' Specification Design Pattern

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.

Decorator Design Pattern Application Tests

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<TEntity> 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, <a href="https://msdn.microsoft.com/en-us//library/bb383977.aspx">this is not the intended usage of the extension methods</a> so you should carefully analyse the pros and cons of the approach.

LINQ Specification Design Pattern

LINQ Specification Design Pattern

LINQ Specification Design Pattern UML Class Diagram

LINQ Specification Design Pattern Class Diagram

LINQ Specification Design Pattern Participants

The classes and objects participating in LINQ 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

ISpecification and Specification stay the same as in the basic implementation of the pattern. You can find their code in my previous article about the Specification Design Pattern. As stated above, the need of the concrete specifications is eliminated through the introduction of the new ExpressionSpecification class.

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);
    }
}

The expression variable holds the core business rule, passed as a C# Func parameter (LINQ expression) to the constructor.

We can initialize the new type of specifications directly in the page object’s constructor.

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; }
}

You can reuse the specifications even only with the help of this class. Take the specifications public properties of the page objects or pass them as parameters to the asserts. However, we can extend the reuse even more. Even if you expose them as public properties, you cannot reuse their chaining rules.

So we can create a new specially designed class called OrderTestContext. It will hold the required input information for the order completion process- the purchase test input and two boolean properties.

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.

So Far in the "Design Patterns in Automated Testing" Series

  1. Page Object Pattern
  2. Advanced Page Object Pattern
  3. Facade Design Pattern
  4. Singleton Design Pattern
  5. Fluent Page Object Pattern
  6. IoC Container and Page Objects
  7. Strategy Design Pattern
  8. Advanced Strategy Design Pattern
  9. Observer Design Pattern
  10. Observer Design Pattern via Events and Delegates
  11. Observer Design Pattern via IObservable and IObserver
  12. Decorator Design Pattern- Mixing Strategies
  13. Page Objects That Make Code More Maintainable
  14. Improved Facade Design Pattern in Automation Testing v.2.0
  15. Rules Design Pattern
  16. Specification Design Pattern
  17. Advanced Specification Design Pattern

 

If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!

Source Code

References

The post- Advanced Specification Design Pattern in Automated Testing appeared first on Automate The Planet.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Anton Angelov
CEO Automate The Planet
Bulgaria Bulgaria
Anton Angelov is an IT Consultant and Quality Assurance Architect at Innovative Lab. He is passionate about automation testing and designing test harness and tools, having the best industry development practices in mind. In addition, he is an active blogger and the founder of Automate The Planet. He strives to make the site one of the leading authorities in Automation Testing by presenting compelling articles, inspiring ardent discussions amongst the community. He is also one of the most-rated-answer authors of questions about Test Automation Frameworks (WebDriver) on Stack Overflow.

You may also be interested in...

Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01 | 2.8.190306.1 | Last Updated 29 Jan 2016
Article Copyright 2016 by Anton Angelov
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid