In my articles “Strategy Design Pattern” and “Advanced 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. The Strategy Design Pattern follows the Open Closed Principle that states that “Classes should be open for extension, but closed for modification“. Another way to create open for extension classes is through the usage of Decorator Design Pattern. In this publication, I’m going to refactor the code examples from the previously mentioned articles to be even more extendable. The used strategies are going to be “wrapped” through decorators. The Decorator Design Pattern allow us easily to attach additional responsibilities to an object dynamically. I believe that it can be heavily utilized in automation tests because of all its benefits.
If you are not familiar with the above patterns, I suggest you to read my articles about them first, to be able to understand the presented concepts thoroughly. (Especially the ones related to Strategy Design Pattern).
The Decorator Design Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
- You can wrap a component with any number of decorators.
- Change the behavior of its component by adding new functionality before and/or after method calls to the component.
- Decorator classes mirror the type of the components they decorate.
- Provides an alternative to subclassing for extending behavior.
Abstract UML Class Diagram
The classes and objects participating in this pattern are:
- Component – Defines the interface for objects that can have responsibilities added to them dynamically.
- Decorator – The decorators implement the same interface(abstract class) as the component they are going to decorate. The decorator has a HAS-A relationship with the object that is extending, which means that the former has an instance variable that holds a reference to the later.
- ConcreteComponent – Is the object that is going to be enhanced dynamically. It inherits the Component.
- ConcreteDecorator – Decorators can enhance the state of the component. They can add new methods. The new behavior is typically added before or after an existing method in the component.
Decorator Design Pattern C# Code
Test’s Test Case
The test case of the examples is going to be the same as of the previous articles. 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
2. Click Proceed to Checkout
3. Login with an existing Client
4. Fill Shipping Info
5. Choose a shipping speed
6. Select a payment method
7. Validate the Order Summary
The previous articles explain in details how to automate the whole purchase process. However, to introduce the benefits of the Decorator Design Pattern, only the last step is going to be necessary- Order Summary Validation. In the posts about the Strategy Design Pattern, the prices on the last step of the purchase process are validated through the help of different Validation Strategies that implement the IOrderPurchaseStrategy.
Improved Version Advanced Strategy Design Pattern Applied
The usage of the PurchaseContext is not so straightforward as you can see from the code below.
new PurchaseContext(new SalesTaxOrderPurchaseStrategy(), new VatTaxOrderPurchaseStrategy(), new GiftOrderPurchaseStrategy()).PurchaseItem(itemUrl, itemPrice, clientLoginInfo, clientPurchaseInfo);
Different prices validations mix is achieved through the iteration of the initialized strategies. However, the disadvantage of the provided solution is that for every new method in the IOrderPurchaseStrategy interface, you need to create a new one with a “foreach” statement in the PurchaseContext class. Also, personally I believe that the initialization of the PurchaseContext in the test method is a little bit unreadable.
If you don’t understand the above code examples thoroughly, you can find more detailed explanations in my articles about the Strategy Design Pattern- “Strategy Design Pattern” and “Advanced Strategy Design Pattern“.
One of the resolutions of the initialization problem of the PurchaseContext is to create more strategy classes that combine the different behaviors, e.g., VatSalesTaxOrderPurchaseStrategy, SalesTaxGiftOrderPurchaseStrategy, GiftOrderPurchaseStrategy,
NoTaxesOrderPurchaseStrategy, etc. But as you can see this escalated quickly- a typical example of a class explosion.
If you need to add additional validators, you will have to add a couple of more classes to achieve the mixing behavior. Here is where the Decorator Design Pattern comes to play. The attached behavior through inheritance can be determined only statically at compile time. However, through the help of composition the decorators can extend the component at runtime.
Specific UML Class Diagram
The classes and objects participating in this pattern are:
- OrderPurchaseStrategy (Component) – Defines the interface for all concrete strategies that are going to validate the different prices on the last step of the purchasing process.
- OrderPurchaseStrategyDecorator (Component Decorator) – The decorator has an instance variable that holds a reference to the OrderPurchaseStrategy. Also, contains another useful info that is going to be used by the concrete decorators to calculate the different expected amounts.
- TotalPriceOrderPurchaseStrategy (ConcreteComponent) – It is a descendant of the OrderPurchaseStrategy, and it is used to verify the total cost of the order.
- VatTaxOrderPurchaseStrategy (ConcreteDecorator) – Can extend the concrete order purchase strategies. Adds a new logic for validating the VAT Tax of the order and also adds the new tax to the total price.
Refactor Purchase Strategies to Support Decorator Design Pattern
The base class for all concrete strategies and their decorators is the OrderPurchaseStrategy.
It holds only two abstract methods.
CalculateTotalPrice – Returns the total price of the order. It depends on the applied taxes and discounts because of that every strategy should implement it.
ValidateOrderSummary – Validates all prices on the order summary page- total price, taxes, discounts, etc.
The first concrete component in the example is the TotalPriceOrderPurchaseStrategy that verifies the correctness of the total price.
To be able to add a new behavior at runtime dynamically, all decorators need to derive from the class OrderPurchaseStrategyDecorator.
This abstract class holds a couple of relevant variables. The most prominent one is orderPurchaseStrategy that is initialized in the constructor. It contains a reference to the object that is currently extended. The other variables are used for the computations of the different expected amounts.
If we want to add logic to the above strategy, for example- application of VAT Tax and its verification. We can use the VatTaxOrderPurchaseStrategy, which in its essence is a decorator that is capable of extending other purchase strategies.
The VatTaxOrderPurchaseStrategy is a descendant of the OrderPurchaseStrategyDecorator. Further, it overrides its methods. The interesting part is that the total price is calculated through a method recursion. First the total amount is determined by the concrete component (order purchase strategy), and then the computed VAT tax is added to it.
The same recursion technique is used for the validation of the order summary UI. Before anything else, the ValidateOrderSummary methods of all extended strategies are going to be executed and after that the VAT tax is verified.
The sales tax can be checked through a similar decorator.
The only difference between the latter and the former is how the tax is determined.
Usage of Decorated Strategies PurchaseContext
The following code is now missing in the improved version.
Now the PurchaseContext holds only one reference to the OrderPurchaseStrategy and employs it to verify the total amount and all other prices on the order summary page.
Decorator Design Pattern Usages in Tests
The most prominent part of the above code is how the order purchase strategies are decorated and utilized by the PurchaseContext.
OrderPurchaseStrategy orderPurchaseStrategy = new TotalPriceOrderPurchaseStrategy(itemPrice);
orderPurchaseStrategy = new SalesTaxOrderPurchaseStrategy(orderPurchaseStrategy, itemPrice, clientPurchaseInfo);
orderPurchaseStrategy = new VatTaxOrderPurchaseStrategy(orderPurchaseStrategy, itemPrice, clientPurchaseInfo);
new PurchaseContext(orderPurchaseStrategy).PurchaseItem(itemUrl, itemPrice.ToString(), clientLoginInfo, clientPurchaseInfo);
First a TotalPriceOrderPurchaseStrategy is instantiated. Then it is passed to the constructor of the SalesTaxOrderPurchaseStrategy, this way it is extended and the sales tax is going to be added to the total price. The same is done for sales tax strategy; a new VatTaxOrderPurchaseStrategy decorator is initialized. Finally, the total price is going to be equal to the item price plus the sales tax plus the VAT tax.
Pros and Cons Decorator Design Pattern
– Decorators can result in many small objects, and overuse can be complicated.
– Can complicate the process of instantiating the component because you not only have to instantiate the component but wrap it in some decorators.
– It can be complicated to have decorators keep track of other decorators because to look back into multiple layers of the decorator chain starts to push the decorator pattern beyond its actual intent.
– Can cause issues if the client relies heavily on the components concrete type.
+ Provide a flexible alternative to subclassing for extending functionality.
+ Allow behavior modification at runtime rather than going back into existing code and making changes.
+ Help resolve the Class Explosion Problem.
+ Support the Open Closed Principle.
So Far in the "Design Patterns in Automated Testing" Series
- Page Object Pattern
- Advanced Page Object Pattern
- Facade Design Pattern
- Singleton Design Pattern
- Fluent Page Object Pattern
- IoC Container and Page Objects
- Strategy Design Pattern
- Advanced Strategy Design Pattern
- Observer Design Pattern
- Observer Design Pattern via Events and Delegates
- Observer Design Pattern via IObservable and IObserver
- Decorator Design Pattern- Mixing Strategies
- Page Objects That Make Code More Maintainable
- Improved Facade Design Pattern in Automation Testing v.2.0
- Rules Design Pattern
- Specification Design Pattern
- Advanced Specification Design Pattern
If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!
All images are purchased from DepositPhotos.com and cannot be downloaded and used for free.
The post- Decorator Design Pattern in Automation Testing appeared first on Automate The Planet.