I wrote this as a reply to user RHarris’s question on StackOverflow, but thought it might be easier to find here too. The login scenario and the access record here are adapted from their question.
What contexts should we include in a scenario?
Scenario 1: A user signs in with valid credentials Given the Sign In page is displayed When the user enters a username and password And the user submits the request Then the user should be directed to the Home page
I find it helps to think of scenarios not as tests, but as living documentation that helps to illustrate how the system behaves and why that’s valuable.
If you can tell that the user isn’t locked out and isn’t disabled in that first scenario, then you don’t need to include those steps.
For something like logging in with an active, enabled account this is obvious, but it might not be for something like a trader staying within their trading limits, or a fetal heartbeat running at a healthy rate, or a comment that hasn’t yet reached the reporting threshold. You can decide whether to include those kind of contexts pragmatically.
I prefer though to move documentation that doesn’t change or doesn’t change very often, like core domain concepts, outside of the scenarios. Perhaps it can be at the blurb at the top of the scenario file, or on a team wiki somewhere that new joiners get to read before they code. It doesn’t have to be in the scenario.
(Obviously you would need to include the contexts which cause the failure in any failure scenario though!)
What about side-effects and other outcomes?
... Then the user should be directed to the Home page And the logs should record their access.
All outcomes that matter do so because at some point, they become visible or have effects beyond the system whose behaviour is covered in the scenario.
For instance, when I get money from a cash machine, my account is also debited. I might not see that as part of the behaviour, but it does have to happen. That’s because there’s another stakeholder involved in the scenario – the bank.
Or perhaps, when Fred gets a refund on his microwave, the microwave is put back into stock and the stock count is incremented. Fred, the till operator and the stock controller are all stakeholders here.
In the case where we’re adding something to the logs, there’s a stakeholder who’s going to use those logs for something else. Having an awareness of that value can help us work out why we’re logging, and describe that outcome from the POV of the stakeholder who finds it valuable.
If the outcomes can be shipped independently and still be valuable, they can appear in separate scenarios. I’m pretty pragmatic about this, so I might put them in the same scenario to start with, then refactor them out later once the number of “stock control” scenarios means that it should probably have its own feature files.
If the outcomes can’t be shipped independently, which is usually the case with anything transactional, I like to see those outcomes appear together in at least one scenario, to make it obvious that they’re related. They don’t have to appear in every scenario. For instance, once we’ve written the scenario where the user doesn’t have funds so their account doesn’t get debited, we probably don’t have to mention the account again in any other cash withdrawal failure scenarios.
This perspective of multiple stakeholders is also related to the BDD concept of “Outside-In”, where all the behaviour we add to the system is done to provide a value to some stakeholder through some interface.
What about interactions with other users or with time?
Sometimes – very occasionally – we need more than one when to describe what’s happening, and the example of a login counter being incremented is a great one to use.
The value of a login counter is only that it disables the account after 3 attempts (for instance). It has no value in and of itself. It’s an implementation detail. If we made this into an outcome in a scenario, we’d be coupling ourselves to that implementation, which wouldn’t be great. So having two scenarios – one to describe how the login counter works and one to show why it’s valuable – doesn’t feel right since one of them isn’t describing valuable behaviour:
Given Clarence logged in successfully last time When Clarence enters his password incorrectly Then the login counter should be incremented. Given Clarence's login counter was incremented twice When Clarence enters his password incorrectly Then his account should be disabled.
Yuk. Let’s not do that.
The only value in that counter is from the interaction of Clarence’s behaviour with Clarence’s future (or past) behaviour. So we could have something like:
Given Clarence logged in successfully last time When he enters his password incorrectly And enters his password incorrectly And enters his password incorrectly Then his account should be disabled.
Of course, that’s a bit of a mouthful, so we’d probably just say:
Given Clarence logged in successfully last time When he enters his password incorrectly 3 times Then his account should be disabled.
We can do that because it’s the same thing happening. Some interactions though involve different things happening (“time passing” is another one I frequently encounter).
Given Clare is editing trade 12345 When Stephen edits and saves that trade And Clare tries to save that trade Then she should be told that Stephen has already edited it.
In this case of course Clare’s interaction is different to Stephen’s, since her attempt to save fails.
Note the use of tries here to indicate failure; another example of how I use assumptions. If it doesn’t say tries we can assume that the event was successful. The alternative would be:
When Clare saves the trade successfully
Unless a successful outcome is somehow surprising and not the norm, this would get a bit repetitive, so I prefer to use nothing for the default and tries for failure. Having a difference between them is important from an automation perspective since it lets us do things like move the automated workflow to an error page instead of the confirmation page.
It also reads quite nicely. Which is pretty much why we’re trying to use these English language tools anyway.