6 BDD Example - essenius/AcceptanceTesting GitHub Wiki
Acceptance Test Driven Development (ATDD) is a very powerful technique to clarify requirements by converting them into test cases, and Behavior Driven Development (BDD) is a specific kind of ATDD which uses the Given-When-Then format (a.k.a. Gherkin) to specify test cases. It is called this way because it gives examples of desired system behaviors.
The Gherkin format is so useful because business oriented people can relate to it much more than typical test patterns like arrange-act-assert.
In a nutshell, it works as follows:
Given <a certain context>
When <a certain event happens>
Then <I expect the following outcome>
The crucual part is that the test cases need to be created before the code is written. This is so important because the test cases become part of the acceptance criteria; in other words they become a proxy for the requirements. It has been shown many times that the thinking process that needs to happen to create good test cases is one of the best defect preventers known. And it is always better (cheaper, more efficient, less waste) to prevent defects than to have to find and correct them.
To make things abundantly clear: if your process involves writing the test cases after the code was written, you are not doing BDD.
This page show an example, hopefully clarifying why the approach is useful. We start with the user story (which is, as any user story, much too ambiguous to be called a requirement):
As a plant inspector, I need to be able to detect a ¼ inch defect in a pipe section in order to ensure plant safety.
The product owner (PO) explains that ultrasonic sensors are used, which produce logs with sensor data. There are algorithms that can detect defects based on the logs, and these algorithms must be implemented. In BDD, we start the process by defining expectations. First, we ask, “The story states ‘a ¼ inch defect’. Is it correct to assume that it should also find larger ones?" The PO confirms. So, we create a first set of expectations in FitNesse and show that to the product owner:
Given a sensor file of a pipe section without defects
When I search for defects
Then I should find 0 defects
Given a sensor file of a pipe section with a defect of 0.25 inch
When I search for defects
Then I should find 1 defect
Given a sensor file of a pipe section with a defect of 0.24 inch
When I search for defects
Then I should find 0 defects
Given a sensor file of a pipe section with a defect of 0.26 inch
When I search for defects
Then I should find 1 defect
Basically, we take 4 sensor files, one of a pipe section without defects, and three with defects of 0.25”, 0.24” and 0.26”, respectively. We expect only the defects of 0.25” and 0.26” to be found. The PO immediately sees a misinterpretation. It’s not enough to only know whether there is a defect or not; the location of the defect in the pipe section is also needed. A pipe section is cylindrical, and a location is indicated by two numbers: the distance from the side in mm (across the linear dimension), and the angle in degrees (from vertical). So, in the next version of the test, we take sensor files with known defects at specific locations, and we expect those to be found if the defect is at least 0.25”.
Given a sensor file of a pipe section with a defect of 0.26 inch at location 270 at 30 degrees
When I search for defects
Then I should find 1 defect at location 270 at 30 degrees
A developer asks: "So, how do we actually detect defects?” The product owner explains: “Well, that depends on the type of defect. There are cracks, punctures, bad welds, and there is corrosion, and they all have different algorithms. Let’s leave corrosion out of scope for the time being because that is a bit more complex."
Since the type of defect appears to be important, we update our expectations to reflect the requirement:
Given a sensor file of a pipe section with a puncture of 0.24 inch at location 270 at 30 degrees
When I search for defects
Then I should find 0 defects
Given a sensor file of a pipe section with a crack of 0.25 inch at location 230 at 270 degrees
When I search for defects
Then I should find 1 defect at location 230 at 270 degrees
Given a sensor file of a pipe section with a bad-weld of 0.26 inch at location 20 at 180 degrees
When I search for defects
Then I should find 1 defect at location 20 at 180 degrees
Given a sensor file of a pipe section with a puncture of 0.27 inch at location 125 at 85 degrees
When I search for defects
Then I should find 1 defect at location 125 at 85 degrees
Note: Corrosion is out of scope
“Wait a minute,” says the PO. “There can be more than one defect in a pipe section. Currently we can only detect one. We need to be able to detect all of them.” We translate that into given-when-then as:
Given a sensor file of a pipe section with 2 defects of at least 0.25 inch
When I search for defects
Then I should find 2 defects
Given a sensor file of a pipe section with 2 defects where only one is at least 0.25 inch
When I search for defects
Then I should find 1 defect
Now, the PO agrees that the current set of expectations are a good representation of what she wants the system to do, and the team has confidence that they can implement this in the sprint. Of course, these are mainly positive test cases and don’t necessarily achieve sufficient coverage.
So, the tester will now go off and elaborate the test cases using the techniques described earlier in this chapter, while the development team starts their test-driven development cycle to implement the story, and the PO ensures that all the necessary sensor logs are provided.
It is hopefully obvious how a quick session to nail down test cases resulted in ironing out a couple of misinterpretations that could have led to a lot of rework. We now have a set of unambiguous, testable and binding requirements that are specified in BDD language, which we can execute as test cases. We know that we are done with this requirement when all the tests pass.
We haven't discussed any tools on this yet. BDD is not primarily about tools, it is about the process that is being followed. As said, the crucial part is that the test cases are created upfront. But it is also important that they can be put in the system on day 1, and can get executed on day 1. FitNesse supports that using Plain Text Tables. With that, it is possible to execute these plain text specifications in Gherkin format before a single line of code has been written.