Guidelines For Examples and Data Tables
The most important guideline to remember is to ensure that each Behaviour Specification remains readable, easily understood and worded in everyday business language. Just because Gherkin allows for data tables to be specified, it does not mean that they always should be used. A data table after a step, or an "Examples" table at the end of a scenario has the potential to reduce the readability of a behaviour – so beware!
When using a table, ensure that the type of data specified does not jeopardize the single-purpose focus of that Behaviour Specification. For instance, it is unwise to encode multiple business rules into the table, as each business rule, for the sake of clarity, really should be its own, separate Behaviour Specification. Please note the difference between a "business rule" and individual pieces of logic that when combined may be encompassed within and make a single business rule. Tables are most useful in circumstances to provide examples of key data for specifying a single business rule.
Use general good testing practices and common sense when specifying data in a table:
- Data that is irrelevant to the behaviour should not be specified;
- The data specified should not be linked magically to a specific test environment, as the test should be executable in any environment. Use dynamic data for Integration Tests;
- Data should only exist in a table if it is necessary to illustrate or trigger the conditional logic or branch paths of a business rule;
- Data generally does not need to be repeated in the table – repeated data that never changes indicates that the behaviour is not written as simply and concisely as it could be; and
- If possible, abstract the data into business language rather than specifying technical details.
Example Table 1
In this example, there is a manufacturing business where orders for products are taken and processed. In this business there is a software component that accepts an order into the system. A single validation-style business rule in that component could be related to ensuring that a mandatory product code has been provided for each order item, and that the product code is "valid".
Digging deeper into the technical understanding of this business rule, there are two interesting points here:
- The mandatory product code, in this case, is a string data type, and thus for it to be "provided", the value must not be null ("not specified") and it must not be an empty string with a length of zero characters ("blank").
- The word "valid" can sometimes be too general and cover multiple conditions depending on the business context, so be careful. In this case however, it simply means that the product code relates to a real product that is listed in the inventory.
We could thus decompose this business rule into three statements:
- Should error when an order item has a product code that is not specified;
- Should error when an order item has a product code that is blank; and
- Should error when an order item has a product code that does not refer to a listed inventory item.
One could create a Behaviour Specification for each of those statements. However, those statements really refer to internal implementation details about the one, single business rule – that the product code must be valid. This is exactly the type of situation where I recommend using a table.
Below is an example Behaviour Specification for this business rule.
@UnitTest Scenario: [Purchasing-ProcessingOrders-Ordering-Service]-010 – Primary/Positive – Should error when an order item has an invalid product code Given an order with an order item where the product code is invalid due to it being <An Invalid Product Code Situation> When the order is submitted for processing Then there is a notification that the product code is invalid Examples: | An Invalid Product Code Situation | |-----------------------------------| | Not specified | | Blank | | Not in the inventory |
This type of Behaviour Specification is reasonably simple for a developer to implement as a Unit Test.
Please note the language used in the Behaviour Specification – it is obvious, self-explanatory and concise. The description tells you, in natural English, that the behaviour is about invalid product codes. Also note that the information in the Examples table also is in natural English and is quick and easy to understand. A table that requires more than a few seconds to interpret or reverse engineer from actual data values back into business language is generally not a desired table!
Example Table 2
Here is another perfectly valid example Behaviour Specification for a service that performs the functionality of the "AND" logical operator of a calculator. This example is more similar to the type of samples generally found by a quick search, and is an interesting contrast to the previous because of its different nature.
@UnitTest Scenario: [Calculations-Service]-010 – Primary/Positive – Should correctly calculate the logical AND of two binary numbers Given the first binary number of <Number1> And the second binary number of <Number2> When the logical AND operator is applied to both binary numbers Then the result is <AND Result> Examples: | Value1 | Value2 | AND Result | |--------|--------|------------| | 0 | 0 | 0 | | 0 | 1 | 0 | | 1 | 0 | 0 | | 1 | 1 | 1 |
Again, the language used in the Behaviour Specification is as natural as possible to the business domain.