Don’t get me wrong, I love Gherkin.
Behavior driven development has done some good things and cucumber, specflow, JBehave, NBehave, Jasmine, et al. have done wonders for improving test readability and communicating tests in business language.
You know “Given, When, Then”
First of all, let’s not kid ourselves. That’s not English, that’s programming syntax. Change “Given” to “Let” “When” to “If” and keep “Then” just the way it is and you’ve just invented BASIC. Or a small subset of BASIC with regular expressions. Or rather a small subset of regular expressions.
You get rid of line numbers, but you also get rid of punctuation — which some people feel is a good trade off, because business people (who never read tests anyway) are afraid of numbers and punctuation.
Enough ranting, though.
Here’s an example of a Gherkin test:
Given some precondition When some action is performed Then some expected result should be verified.
It sounds good enough. Very chronological and logical… to a procedural programmer.
Don’t get me wrong. I love procedural programming. I first learned to program procedurally (in BASIC). But we can do better than that these days.
Here’s a function in plain English:
When some action is performed Given some data Then some result should be returned.
Notice the difference?
The same function with different data creates different outcomes. The function — action — can be reused and only the inputs and outputs change.
Some people try to handle this with Gherkin language by placing the data — the conditions — in the “When” segment. I’d say this is a common approach.
Scenario: Given everything is normal When some data exists Then some expected result should be verified.
Both the preconditions and action are implied.
A common pattern that tries to get data reuse is to create a scenario outline with examples:
Scenario Outline: Given some <precondition> When I act on some <data> Then I should have some <expected result> Examples: | precondition | data | expected result |
My first complaint is that if you have multiple preconditions, you should probably have multiple tests.
The second is that you can have a proliferation of data. This is the same problem Fitnesse ran into.
The third is that the action is still implied — sometimes stated explicitly scenario or the feature.
The action is what you want to test. It should be front and center. It is reusable. It takes an input and has a variable output based on it.
The scenario is the data. The preconditions if you will. But the preconditions are just data that is acted on to “setup” the action under test.
The expected result is tied to the scenario.
My proposal is simply that we write our tests in a slightly different way, which allows for clearer visibility of coverage and scenarios.
Rather than a scenario with a:
- Expected Result
We should have an action with a:
- Input data
- Expected Output
Action: When I do something Given some scenario Then I should observe some expected result
I think this leads to more usability and better identified the difference between precondition, action, and data.
Action: When I do something
Given some scenario
For some <input>
Then I should observe some <expected result>
| Input | Expected Result |
It also makes implementation of tests easier. You now can focus on creating reusable fixtures — this is one thing Fitnesse got right. It makes for better data drive tests as well. Where your examples are come from your data provider. Which allows creating more scenarios.
An action is reusable with multiple scenarios. This is the setup.
A test has one or more scenarios and has one or more data values that each have one expected result per data value, per scenario.