Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / testing

Advanced Strategy Design Pattern in Automation Testing

4.73/5 (8 votes)
7 Oct 2015Ms-PL5 min read 15.5K  
Elaborate further on the Advanced Strategy Design Pattern in automation tests. Usages such as combining multiple strategies in one test or test validations.The post Advanced Strategy Design Pattern in Automation Testing appeared first on Automate The Planet.

Introduction

In my last article, “Strategy Design Pattern“, I explained the benefits of the application of Strategy Design Pattern in your automation tests. Some of the advantages are more maintainable code, encapsulated algorithm logic, easily interchangeable algorithms and less complicated code.

In this part of the series, I’m going to extend my ideas about the application of the Strategy Design Pattern in automation tests. In my previous examples, I presented to you code samples where the tests used only one strategy at a time. Here, I’m going to show you how to apply the pattern so that you will be able to use multiple strategies at once. Another advanced usage that I want to demonstrate is the use of the Strategy Design Pattern, to validate the tests prerequisites.

Advanced Strategy Design Pattern

Advanced Strategy Design Pattern C# Code

Test’s Test Case

The test case of the examples is going to be the same as of the previous article. The primary goal is going to be to purchase different items from Amazon. Also, the prices on the last step of the buying process should be validated- taxes, shipping costs, gift wrapping expenses, etc.

  1. Navigate to Item’s Page

    Amazon Items Page

  2. Click Proceed to Checkout
  3. Login with an existing Client

    Login Existing Client Amazon

  4. Fill Shipping Info

    Fill Shipping Info Amazon

  5. Choose a shipping speed
  6. Select a payment method

    Select Payment Method Amazon

  7. Validate the Order Summary

    Validate Order Summary Amazon

The difference between this article’s examples and the previous ones is going to be that there is a need sometimes to mix multiple strategies in one test. For instance, buy an Amazon item with different billing and shipping information and so paying VAT and Sales taxes in a single purchase. Or even add a gift wrap to the same shopping cart. In my last post’s examples, these operations have been isolated in different strategy classes. The primary goal of today’s refactoring is going to be to extend the code to be able to operate with multiple strategy classes at once.

I have slightly modified the strategy interface with the addition of a new validation method.

C#
public interface IOrderPurchaseStrategy
{
    void ValidateOrderSummary(string itemPrice, ClientPurchaseInfo clientPurchaseInfo);

    void ValidateClientPurchaseInfo(ClientPurchaseInfo clientPurchaseInfo);
}

This method is going to be used to validate the test prerequisite data passed to the pattern’s class. In SalesTaxOrderPurchaseStrategy, if the shipping country is not United States, an ArgumentException is thrown because the other methods of the class won’t be meaningful. The sales tax is applicable only for US.

C#
public class SalesTaxOrderPurchaseStrategy : IOrderPurchaseStrategy
{
    public SalesTaxOrderPurchaseStrategy()
    {
        this.SalesTaxCalculationService = new SalesTaxCalculationService();
    }

    public SalesTaxCalculationService SalesTaxCalculationService { get; set; }

    public void ValidateOrderSummary(string itemsPrice, ClientPurchaseInfo clientPurchaseInfo)
    {
        States currentState = (States)Enum.Parse(typeof(States), clientPurchaseInfo.ShippingInfo.State);
        decimal currentItemPrice = decimal.Parse(itemsPrice);
        decimal salesTax = this.SalesTaxCalculationService.Calculate
		(currentItemPrice, currentState, clientPurchaseInfo.ShippingInfo.Zip);

        PlaceOrderPage.Instance.Validate().EstimatedTaxPrice(salesTax.ToString());
    }

    public void ValidateClientPurchaseInfo(ClientPurchaseInfo clientPurchaseInfo)
    {
        if (!clientPurchaseInfo.ShippingInfo.Country.Equals("United States"))
        {
            throw new ArgumentException("If the NoTaxesOrderPurchaseStrategy is used, 
		the country should be set to United States because otherwise 
		no sales tax is going to be applied.");
        }
    }
}

The same logic is implemented in the VatTaxOrderPurchaseStrategy, GiftOrderPurchase strategies. The only addition to these classes is the ValidateClientPurchaseInfo method. It can be used to validate that your strategies are utilized in the correct manner. There are a lot of companies where quality assurance automation engineers write the core framework (strategies, context classes) while the tests are written by less technical people. So such preventative measures can be considered in cases like that.

Reconstruct Text Strategies Context

First Version Basic Strategy Design Pattern Applied

C#
public class PurchaseContext
{
    private readonly IOrderValidationStrategy orderValidationStrategy;

    public PurchaseContext(IOrderValidationStrategy orderValidationStrategy)
    {
        this.orderValidationStrategy = orderValidationStrategy;
    }

    public void PurchaseItem(string itemUrl, string itemPrice, 
    ClientLoginInfo clientLoginInfo, ClientPurchaseInfo clientPurchaseInfo)
    {
        ItemPage.Instance.Navigate(itemUrl);
        ItemPage.Instance.ClickBuyNowButton();
        PreviewShoppingCartPage.Instance.ClickProceedToCheckoutButton();
        SignInPage.Instance.Login(clientLoginInfo.Email, clientLoginInfo.Password);
        ShippingAddressPage.Instance.FillShippingInfo(clientPurchaseInfo);
        ShippingAddressPage.Instance.ClickContinueButton();
        ShippingPaymentPage.Instance.ClickBottomContinueButton();
        ShippingPaymentPage.Instance.ClickTopContinueButton();
        this.orderValidationStrategy.ValidateOrderSummary(itemPrice, clientPurchaseInfo);
    }
}
By the way, during my research for the "Design Patterns in Automation Testing" series, I always first read about the presented pattern in several books. One of them that you might want to check is "Head First Design Patterns" by Eric Freeman. The author uses a very unique methodology for presenting the material that I haven't found anywhere else. Probably most of you will like it. For the more hardcore fans that might find the book too easy, I recommend the bible of the design patterns-"Design Patterns- Elements of Reusable Object-Oriented Software". It will change your way of thinking about object-oriented design.

Advanced Strategy Design Pattern Purchase Context Reconstruction

Improved Version Advanced Strategy Design Pattern Applied

C#
public class PurchaseContext
{
    private readonly IOrderPurchaseStrategy[] orderpurchaseStrategies;

    public PurchaseContext(params IOrderPurchaseStrategy[] orderpurchaseStrategies)
    {
        this.orderpurchaseStrategies = orderpurchaseStrategies;
    }

    public void PurchaseItem(string itemUrl, string itemPrice, 
    ClientLoginInfo clientLoginInfo, ClientPurchaseInfo clientPurchaseInfo)
    {
        this.ValidateClientPurchaseInfo(clientPurchaseInfo);

        ItemPage.Instance.Navigate(itemUrl);
        ItemPage.Instance.ClickBuyNowButton();
        PreviewShoppingCartPage.Instance.ClickProceedToCheckoutButton();
        SignInPage.Instance.Login(clientLoginInfo.Email, clientLoginInfo.Password);
        ShippingAddressPage.Instance.FillShippingInfo(clientPurchaseInfo);
        ShippingAddressPage.Instance.ClickDifferentBillingCheckBox(clientPurchaseInfo);
        ShippingAddressPage.Instance.ClickContinueButton();
        ShippingPaymentPage.Instance.ClickBottomContinueButton();
        ShippingAddressPage.Instance.FillBillingInfo(clientPurchaseInfo);
        ShippingAddressPage.Instance.ClickContinueButton();
        ShippingPaymentPage.Instance.ClickTopContinueButton();

        this.ValidateOrderSummary(itemPrice, clientPurchaseInfo);
    }

    public void ValidateClientPurchaseInfo(ClientPurchaseInfo clientPurchaseInfo)
    {
        foreach (var currentStrategy in orderpurchaseStrategies)
        {
            currentStrategy.ValidateClientPurchaseInfo(clientPurchaseInfo);
        }
    }

    public void ValidateOrderSummary(string itemPrice, ClientPurchaseInfo clientPurchaseInfo)
    {
        foreach (var currentStrategy in orderpurchaseStrategies)
        {
            currentStrategy.ValidateOrderSummary(itemPrice, clientPurchaseInfo);
        }
    }
}

There are a couple of significant changes in the above code compared to the first version. The most prominent one is that the strategies instances are now stored in an array.

An unspecified count of strategies can be passed to the constructor of the class as a result of the usage of the params parameter.

C#
public PurchaseContext(params IOrderPurchaseStrategy[] orderpurchaseStrategies)
{
   this.orderpurchaseStrategies = orderpurchaseStrategies;
}

Two new methods are added, where for each registered strategy, a particular method is executed. If you pass two strategies, their two methods are going to be performed successively.

The ValidateClientPurchaseInfo is called for each strategy so that if there are any misconfigured data, it is going to throw an ArgumentException.

Advanced Strategy Design Pattern- Usage in Tests

The usage of the advanced strategy design pattern in tests stays as smooth as before but now you can use multiple strategies combined.

First Version Basic Strategy Pattern Applied

C#
[TestClass]
public class AmazonPurchase_PurchaseStrategy_Tests
{ 
    [TestInitialize]
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

    [TestCleanup]
    public void TeardownTest()
    {
        Driver.StopBrowser();
    }

    [TestMethod]
    public void Purchase_SeleniumTestingToolsCookbook()
    {
        string itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
        string itemPrice = "40.49";
        ClientPurchaseInfo 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"
        })
        {
            GiftWrapping = Enums.GiftWrappingStyles.None
        };
        ClientLoginInfo clientLoginInfo = new ClientLoginInfo()
        {
            Email = "g3984159@trbvm.com",
            Password = "ASDFG_12345"
        };

        new PurchaseContext(new SalesTaxOrderValidationStrategy()).PurchaseItem
			(itemUrl, itemPrice, clientLoginInfo, clientPurchaseInfo);
    }
}

In the first version, it has been possible to pass only a single instance of the IOrderValidationStrategy.

Improved Version Using Advanced Strategy Design Pattern

Improved Version Advanced Strategy Design Pattern Applied

C#
[TestClass]
public class AmazonPurchase_AdvancedPurchaseStrategy_Tests
{ 
    [TestInitialize]
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

    [TestCleanup]
    public void TeardownTest()
    {
        Driver.StopBrowser();
    }

    [TestMethod]
    public void Purchase_SeleniumTestingToolsCookbook()
    {
        string itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
        string itemPrice = "40.49";
        var shippingInfo = 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"
        };
        var billingInfo = new ClientAddressInfo()
        {
            FullName = "Anton Angelov",
            Country = "Bulgaria",
            Address1 = "950 Avenue of the Americas",
            City = "Sofia",
            Zip = "1672",
            Phone = "0894464647"
        };
        ClientPurchaseInfo clientPurchaseInfo = new ClientPurchaseInfo(billingInfo, shippingInfo)
        {
            GiftWrapping = Enums.GiftWrappingStyles.Fancy
        };
        ClientLoginInfo clientLoginInfo = new ClientLoginInfo()
        {
            Email = "g3984159@trbvm.com",
            Password = "ASDFG_12345"
        };

        new PurchaseContext(new SalesTaxOrderPurchaseStrategy(), 
        new VatTaxOrderPurchaseStrategy(), new GiftOrderPurchaseStrategy())
        .PurchaseItem(itemUrl, itemPrice, clientLoginInfo, clientPurchaseInfo);
    }
}

Here you are able to mix multiple strategies - SalesTaxOrderPurchaseStrategy, VatTaxOrderPurcahseStrategy, GiftOrderPurchaseStrategy. If you have to add new strategies to your logic, it is going to be easy to plug them in the existing design. Also, you won’t have to modify the Context class because of the usage of the IOrderPurchaseStategy array and the params parameter.

As a consequence of these actions, your tests’ architecture will follow the Open Close Principle, which states that the code should be open for extension, but closed for modification.

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

 

All images are purchased from DepositPhotos.com and cannot be downloaded and used for free.
License Agreement

The post Advanced Strategy Design Pattern in Automation 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)