A Few Shades of Gherkin For Business Rules

A blog on specflow.org talked about ways to document scenarios for a business rule.   It reminded me about some aspects of blog wrote a few years ago called Six Shades of Gherkin.    Here’s the business rule that was used as the example in specflow.org blog:

A volume discount rule provides 10% off for purchases between 5 and 10 items, 15% for purchases between 10 and 20; and 20% above that.

One of the issues with business rules is making sure they are understood by the entire triad (customer, developer, tester).    In this example,  what is the discount for 10 items?    One way to document is to use a scenario outline that gives the results for each side of each breakpoint.   Another important value is documenting the results at the limits and beyond.

Scenario Outline: Compute discount based on number of items # Shade 1
Given the purchase order contains <items>
When the user checks out
Then the discount offered should be <discount>
Examples:
|items | discount |
|1     |  0%      |
|5     |  0%      |
|6     | 10%      |
|9     | 10%      |
|10    | 15%      |
|19    | 15%      |
|20    | 20%      |
|1000  | 20%      |

I usually suggest using a separate column to give more information as to why the particular values were chosen as shown in Shade 2.  It ties the values into the business rule.  So the scenario outline looks like the following.   The name “notes” could be whatever you decide – “description”, etc.    This column is not used by the scenario, but it could be passed to a step definition and used in an error message.

Scenario Outline: Compute discount based on number of items #Shade 2
Given the purchase order contains <items>When the user checks outThen the discount offered should be <discount>
Examples:
|items | discount |  notes                |
|1     |  0%      | less than 6           |
|5     |  0%      |                       |
|6     | 10%      |  6 to 9               | 
|9     | 10%      |                       |
|10    | 15%      | 10 to 19              |   
|19    | 15%      |                       |
|20    | 20%      |  20 +                 |
|1000  | 20%      | Maximum number of items | 

Using a Scenario Outline is a common way of specifying Gherkin.  The Given/When/Then statements often appear as boilerplate, particularly when there are multiple values.  For example the steps often read “Given something is <something>”, where the name of the header is just repeated.

There can be efficiency issues.  With three step definitions and each definition called for each of the eight rows in the Examples,  there are 24 calls to step definitions.

There is another alternative shade – #3. The table with the headers is the same as for the Examples in #2.   However, the table now is a step table.   The scenario name reflects the business rule.    The step text describes the inputs and the output (or outputs).  I’d like to have a Gherkin keyword called Business Rule, but that’s not available.  Using the * keyword emphasizes that this is just a calculation.

Scenario: Compute discount for purchase order   # Shade 3
* Discount based on number of items in purchase order
|items | discount |  notes                |
|1     |  0%      | less than 6           |
|5     |  0%      |                       |
|6     | 10%      |  6 to 9               | 
|9     | 10%      |                       |
|10    | 15%      | 10 to 19              |   
|19    | 15%      |                       |
|20    | 20%      |  20 +                 |
|1000  | 20%      | Maximum number of items |

Now there is only one call to a step definition, regardless of the number of lines in the table.  This will dramatically decrease the execution time.   That’s important as the number of business rules grows.    The developer needs to write a class with attributes whose names match the column headers.   The step definition will have a for loop to go through each of the rows.

The business rule test will fail if the method does not meet all the expectations.  You just fix the first reported failure and then continue on until all failures are fixed.  Alternatively, you could capture the failures in the loop and do a single assert at the end.

You can do Red-Green-Refactor by commenting out all rows except one and making it pass.  Then uncommenting one line at a time and making all the uncommented rows pass.

In most reporting systems, this version only counts as a single test, but don’t all those values just comprise a test of a business rule?

Here are a couple of other thoughts.  The business rule, as provided by the business should be in the feature file.  It should be re-written if it’s ambiguous.   It could be included as a comment. For example:

# Volume discount for purchase order rule: 
# Provide 10% off for purchases between 5 and 9 items,
# 15% for purchases between 10 and 19; and 20% above that.

In many places, business rules are given an identifier (such as BR5120).  That could be included in the comment or the identifier, so scenarios for a particular business rule can easily be located.  e.g.:

Scenario: BR5120 Volume discount purchase order

Try out a few of these shades and see which one you prefere.