As you probably know in my series “Design Patterns in Automated Testing“, I explain the benefits of applying design patterns in your automation projects. In this article, I am going to share with you the aid that you can gain from the usage of Rules Design Pattern. It can help you to reduce the complexity of your conditional statements and reuse them if needed.



Definition Separate individual rules from rules processing logic .

Allow new rules to be added without the need for changes in the rest of the system. Separate the logic of each individual rule and its effects into its own class. Separate the selection and processing of rules into a separate Evaluator class.

Abstract UML Class Diagram

Participants

The classes and objects participating in this pattern are:

IRule – Defines the interface for all specific rules.

– Defines the interface for all specific rules. IRuleResult – Defines the interface for the results of all specific rules.

– Defines the interface for the results of all specific rules. BaseRule – The base class provides basic functionality to all rules that inherit from it.

– The base class provides basic functionality to all rules that inherit from it. Rule – The class represents a concrete implementation of the BaseRule class.

– The class represents a concrete implementation of the BaseRule class. RulesChain – It is a helper class that contains the main rule for the current conditional statement and the rest of the conditional chain of rules.

– It is a helper class that contains the main rule for the current conditional statement and the rest of the conditional chain of rules. RulesEvaluator– This is the main class that supports the creation of readable rules and their relation. It evaluates the rules and returns their results.

Rules Design Pattern C# Code

Test’s Test Case

Consider that we have to automate a shopping cart process. During the purchase, we can create orders via wire transfer, credit card or free ones through promotions. Our tests’ workflow is based on a purchase input object that holds all data related to the current purchase e.g. type of purchase and the total price.

A sample conditional test workflow for our test logic without any design pattern applied could look like the following code.

The actions that can be performed in the conditions may be- applying coupons or other promotions in the UI, completing order via different payment methods in the UI, asserting different things in the UI or the DB. This test workflow is usually wrapped in a method of a facade or similar class.

The main problem with this code is that it is highly unreadable. Also, another thing to consider is that you might need the same rules in different kind of classes- you may want to use the rule once in a UI Facade and a second time in a DB Asserter class.

Improved Version Rules Design Pattern Applied

This is how the same conditional workflow looks like after the usage of Rules Design Pattern. As you can see, it is tremendously more readable than the first version.

The chain is evaluated once the EvaluateRulesChains method is called. The returned actions are executed in the order of execution of the configured rules. The action associated with a particular rule is executed only if the evaluation of the rules returns success otherwise it is skipped.

Rules Design Pattern Explained C# Code

All concrete rules classes should inherit from the base rule class.

It defines an abstract method that evaluates the current rule and holds the action that will be performed on success.

Here is how one concrete rule looks like.

It can accept as many parameters and data as you need to perform the wrapped condition. It overrides the abstract Eval method where the prime condition is wrapped. If the condition is true, the IsSuccess property is set to true, and the positive rule result is returned. By positive outcome, I mean a result that holds the associated action, not an empty one.

The primary class for rules interpretation is the RulesEvaluator class.

It provides methods for defining the IF, IF-ELSE and ELSE clauses. The IF is declared via Eval method, IF-ELSE through OtherwiseEval and ELSE with OtherwiseDo. Also, it holds the EvaluateRulesChains method that evaluates the entirely configured chain of conditions and executes all associated actions. It works internally with another class called RulesChain.

RulesChain represents a conditional workflow of IF, IF-ELSE and ELSE clauses. It holds the current rule (e.g. IF) and all following rules (e.g. IF-ELSE/ELSE).

Once the EvaluateRulesChains method is executed, the configured rules are evaluated consequently. If the Eval- rule returns success all following OtherwiseEval and OtherwiseDo rules are skipped. If not the next rule in the chain is evaluated and so on. The same pattern is applied as in the typical IF-IF-ELSE-ELSE workflow.

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

Rules Design Pattern Configuration



Private Methods Configuration

There are three types of configurations of rules, mainly related to the Action parameter of the BaseRule class.

The rule-associated action is defined as a private method in the class where the RulesEvaluater is configured. All actions associated with rules can be separated in different private methods.



Anonymous Method Configuration- Lambda Expression

Another way to pass the actions is via anonymous method using а lambda expression.

In my opinion, this approach leads to unreadable code, so I stick to the first one.

Generic Rule Result Configuration

You can create a generic, specific rule where the generic parameter represents a rule result where the associated action is declared. You can use different combinations of the rule and its results classes. However, this approach can lead to a class explosion so you should be careful.

This is how a sample concrete rule result class looks like.

The usage is straightforward.

The same rule is used twice with different actions wrapped in different result classes.

Summary

Consider using the Rules Design Pattern when you have a growing amount of conditional complexity.

Separate the logic of each rule and its effects into its class.

Divide the selection and processing of rules into a separate Evaluator class.