During the last week, there was a Behavior Driven Development (BDD) related question on one of my mailing lists. For me it expressed a large misconception about BDD that is so widespread, and so common. Let’s see what it is, and what to do about it.
Here is the relevant part of the original mail:
Basically, the requirements is to ensure a user is redirected back to the product (eBook) page after they register a new account. Login/registration page is the same page.
GIVEN I am a non-logged in user
AND I am not remembered
WHEN I try to add an item to my “Must read list”
THEN I am directed to the eBook store login/registration page
AND I complete the registration
AND I am redirected back to the eBook page I started from
AND the product will be added to my “Must read list”
Can you spot the problem? Here’s a challenge, before reading further, try to come up with a better solution. Everything’s there that you may need to know to improve it.
Done? Good. Let’s see which smells I can identify.
Impersonalized
“Given I am a non-logged in user” is an impersonalization. It describes the setting of who the “I” is. In most cases, I found that such statements can be reframed to ‘When a non-logged in user tries to add an item to his “Must read” list’ for example. Basically there is no need for the “I” in the scenario, and often it leads sentences like the one above. Replace “I” with the user you are talking about.
Passive voice
I was taught that passive voice should not be used. Like the previous sentence, by using more active statements, you get way better scenarios. “Then the website redirects the user to the registration/login page”, “And the “Must read” list contains the product”, … Active voice improves the readability of the scenario to a big deal.
Details
The scenario is very detailed. It contains a lot of unnecessary details. For example the fact that the user is not logged in, includes that he is not remembered to me. The fact that I am redirected to the login/registration page, and login, and am redirected back to the original page, seems to be a single action that happens from the user goal perspective. Overall this leads to a long scenario. Over the years I have seen scenarios that consist of 100 lines of step definitions and more. Usually there is a pretty good reason why people come up with scenarios like this. I am happy with this, as long as they don’t claim to do ATDD or BDD just because they use a tool developed for BDD.
This leads me to the real problem, the process problem.
“Our testers can’t write code”
Now, here’s the problem, whether you like it or not: your testers are writing code in those BDD tools. They are programming. This is widespread knowledge. For example, lesson 118 in Lessons Learned in Software Testing says it clearly.
What’s even worse with the illusion that your testers can’t write code, and asking them to automate tests in such a way is this: The tools are not as powerful as a developer’s IDE. Even if they were, our industry would still be ending up with unskilled people writing code. I have worked with unskilled programmers over the years. Usually they produced what is known as Legacy Code. Code that starts to slow you down. Did you ever experience something like that in test automation? Test automation that slowed you down?
That leads us to the next problem: When your business relies on the production code, and that code slows you down dramatically, what do you do about it? Exactly, you get someone in helping you with that problem since it has a large business impact. Whether you believe that a single day training helps you, or someone should coach you on how to do it, doesn’t matter. You do something about it to improve it.
What happens when you face a problem with your test automation code that slows your business intake down? Very few do the same, and get someone in to help them. More often, you curse test automation as the culprit, give up your test automation efforts, and stick with manual testing. If that slows you down, you skip tests. Reframed: you skip addressing some of your risks. I can’t evaluate whether you really can allow yourself to do that.
There really is only one way out of this: train your test automators in enough coding so that they can write down reasonable scenarios, are aware of the shortcomings, and have enough design background to refactor the code smells they are creating, and can transfer that knowledge to their scenarios.
Forgotten business goal
The biggest problem with the initial description lies in the mismatch between the scenario, the stuff with the given/when/then in it, and the description in front of that. Let’s take another look:
Basically, the requirements is to ensure a user is redirected back to the product (eBook) page after they register a new account. Login/registration page is the same page.
GIVEN I am a non-logged in user
AND I am not remembered
WHEN I try to add an item to my “Must read list”
THEN I am directed to the eBook store login/registration page
AND I complete the registration
AND I am redirected back to the eBook page I started from
AND the product will be added to my “Must read list”
The business goal of being redirected back to the product page is not obvious in the scenario. If you face that scenario later, then you will have a hard time understandings the business goal. Why couldn’t we just express that? It turns out we can:
When I register a new account from a product page
Then I am redirected back to the product page
There are still a bunch of the smells I mentioned. But the original business goal is now clear. Of course, there are now things you need to hide in step definitions, and of course your testers need to be able to write enough code so that they can deal with the situation.
Events
What most people do not know is that BDD consists of a combination of TDD, ATDD, DDD, Specification Workshops, and outside-in development. That said, it helps to take a closer look on the events that are happening, and try to express the outcomes as messages that should be forwards from your different bounded contexts in your application. Let’s see how that situation could turn out for the example:
Given a non-logged in user adds a product to his “Must read” list
When the user registers a new account
Then the user is redirected back to the product page
And the product is added to his “Must read” list
In the given and when parts we now have events that happen inside your domain, and could be fired in your solution. DDD calls these domain events, since they are something relevant that happens in your domain. The then part also consists of events, but these are raised by your solution.
Stop claiming BDD if you are not
That last example serves the point that you shouldn’t claim to be doings BDD if you are not. Just because you found some shiny tool that helps you write fluent text that is executed, and can be used as regression test tool does not mean that you are doing BDD. BDD foremost starts with a conversation with your customer, users, and clients. If you sit down on your own computer and try to build a prototype with a BDD tool, then you are not doing BDD. From my perspective you are not even yet doing ATDD, either. But this is probably a loaded discussion for another day.
Hello Markus,
Thanks for this great aarticle. The part about “our testers can’t write code” is very helpful to me in my discussion at the office about the use of tooling and the amount of schooling a tester needs.
But recarding your example scenario I have a question. You re-write the story and write down;
“Given a non-logged in user adds a product to his “Must read” list,
When the user registers a new account”.
To me the suggests that the user is not a user at all, but also it suggest that he is. Based on the words “non-logged in” I make the assumption that he could login in before, sugesting he is already a user of the app/site.
But the words “new account” suggest he isn’t a user of the app/site.
So I would re-write these to:
Given a user wants to adds a product to his “Must read” list
When the user is new he should register to a new account
When the use is not new he should login
Then the user is redirected back to the product page
And the product is added to his “Must read” list
Can you give any comments to my re-writing so I can learn from this?
Thanks in advance,
Daniel
Good thoughts, Daniel.
One thing that triggers me is the doubled “when”. It looks like there are two actions. This is usually going to hurt you later, so let’s take a look what these two actions are. The two actions appear to be adjacent to each other. Note that “when” in Gherkin is not meant to be conditional, but rather as “at this point” kind of sense. So, I would split it up into two scenarios:
“Given a user wants to add a product to his “Must read” list
And the user is unknown
When he registers a new account
…”
and
“Given a user wants to add a product his “Must read” list
When the user logs in…”
Hi Markus,
That makes sence splitting up scenario’s if they conflicting within one scenario.
Thanks for the clearity & learning oppertunity.
Daniel
Hello Markus,
what do you mean, when you say when is supposed to mean something like ‘at this point’?
I think BDD can be helpful, but we get some problems by using ‘normal’ or ‘spoken’ language. We have so many different interpretations of our written and spoken language that it can become difficult to clearly say what you want to say. It gets more difficult when a tool like Gherkin overloads ‘normal’ semantics with a special technical meaning, I think. You have pointed to this problem in your answer to Daniel.
From a pure linguistical point of view I read your scenarios like this:
when is definitely conditional, because the statement ‘when he registers a new account’ is a condition that has to be achieved before the consequence ‘user is redirected to product page’ can be realized. (an explanation of conditional sentences using when. I’m sorry, it’s only in german. http://www.mein-deutschbuch.de/lernen.php?menu_id=121)
I think there is a semantic connection between both statements which is stronger than a technical ‘at this point’.
Florian,
consider this simplistic example:
Given I input “2” and “+” and “2” in a calculator
When I press the “=” sign
Then the result is shown as “4”
The “When”-part there does not mean the same as:
Given I input “2” and “+” and “2” in a calculator
If I press the “=” sign
Then the result is shown as “4”
And if I don’t do anything then something else will happen
The “When” part is meant in the following way:
Given I input “2” and “+” and “2” in a calculator
At the point in time when I press the “=” sign
Then the result is shown as “4”
The conditional meaning would be the same as an “if” in a programming language, splitting the execution branches depending on whether we follow the if or not. That is not what the “when” part is meant for. Rather, in a DDD architecture system, an event is fired, or an action is raised, and the system reacts based upon those domain events and subsystem actions.
I have blogged about that a while ago, and coined it the GivenWhenThen fallacy: http://www.shino.de/2011/01/21/the-german-givenwhenthen-fallacy/
Hello again,
thanks for the link to your other article. I agree that there is a problem in understand or interpreting the when statement. I think it is dangerous reading a BDD spec in Given, When, Then like a normal language text, although it seems to be designed to be better and easier readable for humans. The missing structure of written language (missing ends of senctences, commata, …) and a collection of keywords which is too small, in my opinion, make it hard to understand a bdd scenario. How is this idea handling those problems:
mark the keywords with a ‘:’ at the end. The important content is the following natural language statement.
I think you need to have a very good knowledge of german or english grammar, good writing and analytical skills to generate a comprehensive spec which contains the important requirements parts.
I see problems with your final solution under the Events section.
Given a non-logged in user adds a product to his “Must read” list
– this should be in past tense.. it should not be an action
When the user registers a new account
Then the user is redirected back to the product page
And the product is added to his “Must read” list
– here we have two outcomes, it should really just be one outcome and should indicate we haven’t properly identified the context and goal of this test
To me this is a workflow situation and you must break them out in to several scenarios:
Feature: eBook Store must read list
Scenario: Adding to must read list requires registration
Given an unauthenticated user
When the user adds product A to the “Must read” list
Then the user is required to register
Scenario: Adding to “Must read” list after registration
Given an unauthenticated user was adding product A to the “Must read” list
When the user registers
Then product A is added to the “Must read”list
What the original example explained was too many different domains: UI interaction, back-end redirection, user registration, and adding products to a must read list. You have to break out the domains and keep the scenarios small and focused. What will happen with even the scenarios above is that you will find there are cross cutting concerns such as “redirect back to the action you were performing if you are not authenticated.” Most likely you can right a feature for that with several edge cases that may include adding a product to ensure the underlying component that implements the feature is working. You then attack cross cutting concerns like the user has logged in or not and do not have to repeat it for every feature.
Deryl,
I certainly agree with your analysis. In this attempt though I wanted to provide folks that are writing scenarios like the original one a step towards a better scenario that they more easily take.
Thanks for the additions.