Applying BDD acceptance criteria in user stories
Don’t get me wrong, the user story narrative is critical to conveying the purpose and value of a user story. Here’s just one of many resources online explaining the advantages of a user story narrative, and how well written user stories help product owners communicate features to the team. But is it not, as important, if not more important, to discuss the acceptance criteria in a user story?
Well written acceptance criteria reduce the requirement-build gap that often falls through the cracks of communication. One of the industry-recognised best practices in writing acceptance criteria is the Behavior-Driven Development (BDD) format. As BDD gained popularity among agile practitioners, some common misconceptions started to appear.
The goal of writing in BDD format
There are many resources out there explaining BDD misconceptions, and its relationship with Gherkin and cucumber automated tests. In essence, we use the BDD format to ensure the syntax detailing the requirements, is close to the syntax an engineer would use to write and execute tests.Like the Agile Manifesto and principles, in which these guidelines explain why we do and think the way we do, BDD is often misinterpreted. For example, how many times have we heard the statement 'we’re not doing documentation because we’re working agile!'
A lightning quick recap of BDD
As this is not another how-to-BDD blog post, I assume we're on the same page as far as the basic principles are concerned.That is:
- 'Given' is the precondition(s), state, parameters relevant to this particular scenario. Setting the scene.
- 'When' is a trigger, or a state change, the thing we’re testing
- 'Then' is the expected outcome(s) of the trigger given the context of the preconditions
Common mistakes anti-patterns in BDD
'Mistakes' is a debatable word in this case, and here’s why. Since we use BDD for its similarity to natural language, it is often thought that if a BDD statement is grammatically correct, i.e. flows off the tongue of a native English speaker, it should be correct.And since we value 'working software over comprehensive documentation', it shouldn’t matter much as long as the team understands it right? Debatable.
So brace yourselves, grab a stress ball, keep sharp objects away, and let’s dive in and look at some of the common BDD anti-patterns.
Not a matter of if, but when
Here’s one misconception that is most commonly found:When the value entered in the Number text box is not numerical
Then an error message “Please enter a numerical value” appear
A better way to write this could be:
When the Form is submitted
Then an error message “Please enter a numerical value” appear.
At this point, you might be thinking “Big deal! You say to-may-to, I say to-mah-to! Right?” Well not really. Imagine writing a similar scenario that validates a text box only if the user is logged in.
If we follow the incorrect example:
Given the value entered in the Number text box is not numerical
When the Form is submitted
Then an error message “Please enter a numerical value” appear
Given the User is logged in ← Condition
And the value in the Number text box changes ← Trigger
When the value in it is not numerical ← Condition? Trigger?
Then an error message “Please enter a numerical value” appears
This further blurs the lines of precondition and trigger, which actually voids the purpose of a clearly defined BDD format.
To explain this point further, if we don’t care about what goes where as long as it is comprehensible, why not just throw away the 'Given' clause entirely?
And the value in the Number text box changes
And the value in it is not numerical
Then an error message “Please enter a numerical value” appears.
To really conform to the BDD format, a better way of writing this could be:
And the value in it is not numerical
When the value in the Number text box changes
Then an error message “Please enter a numerical value” appear
When When When
Following the anti-pattern of mixing up between a precondition (Given) and trigger (When), we sometimes see people writing multiple statements of 'When' using the 'And' operator.Here’s an example where it's incorrect:
When I enter my details
Last Name
Password
Then an account is created
And account name is set as my Email
And a confirmation email is sent to me
So, what exactly is the behavior we’re testing here?
Is it the behavior of entering a First Name? Entering an Email? Entering a password? Or is this testing the behavior of submitting sign up details?
What about the validity of these fields entered? Granted, these questions could be easily answered by a simple conversation with the team. After all, story cards act as a pointer for conversations.
However, imagine these conversations at scale, for every acceptance criteria of every story. Ideally, acceptance criteria should be written as unambiguously as possible, so that we reserve conversation time for more complex matters. There are bigger fish to fry.
The When clause should only contain a single trigger, and the Given clause should list all the conditions that have an impact to that trigger.
For Example:
Given I’m at the sign up form
And all these mandatory fields are entered
- First Name
- Last Name
- Password
Then an account is created
And account name is set as my Email
And a confirmation email is sent to me
The clear distinction between these two examples is that the right example has a clear trigger, i.e. submission of the form; with a clear precondition, i.e. the fields are validated; the wrong example has a sequence of events in the trigger.
Precondition vs sequence of events
While we aim to be unambiguous when writing user stories, we could possibly go overboard and fall into the pitfall of over-explaining.Take the example of a scenario to reconfirm a flight seat selection:
And I chose to skip seat selection at the Seat Selection page
And I selected a meal at the Meal Selection page
When I confirm my details on the Confirm Details page
Then a warning message should appear “Seat will be assigned randomly, proceed”.
Again, at first glance, this looks right, and frankly, it is not hard to write acceptance tests for this. Yet, there is a simpler, and better way of writing the same scenario:
And I had opted to skip seat selection
When I confirm my details on the Confirm Details page
Then a warning message should appear “Seat will be assigned randomly, proceed”
In this example, the precondition section first sets the context, then mentions the only precondition that matters in this scenario.
This simplification is done because in this case, these things do not matter:
- The flow and order in which the user arrives at the Confirm Details Page
- The actions and parameters (other than skipping seat selection) the user has done before this
As unit tests go – we test the smallest piece of individual functionality.
Final thoughts
While I am no self-proclaimed BDD expert or wanting to lecture fellow practitioners on this subject, I am, however, a strong believer that “technically correct is the best kind of correct”.When Dan North pioneered BDD, there were reasons that Given-When-Then were confined to the definition of Precondition-Trigger-Outcome. Some of these reasons extend to test principles like Arrange-Act-Assert and Four-Phase Tests.
After all, a well-written acceptance criteria serves two purposes. Firstly, it is to articulate with clarity to a non-technical audience that the criteria will be used to validate a feature’s behavior. Secondly, and equally important, it is to ensure that this requirement can be easily transformed into building and testing code. BDD happens to be a good medium to address these outcomes.
Given these reasons, I believe that while the readability of the BDD statements is important, there is value in advocating for the right way of writing BDD statements that stick closely to the format of Given Precondition(s) - When Trigger - Then Outcome(s)
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.