Andrew Larcombe

Three BDD antipatterns

An antipattern is described by wikipedia 'as a pattern used in social or business operations or software engineering that may be commonly used but is ineffective and/or counterproductive in practice.' Behavioural Driven Development isn't immune to these, so here are three common antipatterns to watch out for when writing BDD features.

1. 'As a user'

Decorating the outside of each executable specification (i.e. feature) should be some words explaining, in 'user story' form, what the feature is aiming to do. Typically these begin 'As a user I want to', e.g. in a scenario for an online bookstore you might have 'As a user I want to browse books by category'. And whilst this can be easily understood by all project stakeholders, looking at it from another perspective 'User' is too broad a term, homogeonising the varieties of users and their requirements into a single entity - 'the User'. To counter this consider creating a vocabulary of different user types, replacing the generic 'User' with a specific user type whenever you see 'As a user' eg

As a shopper I want to filter books by category

or

As a logged in customer I want to see my recent orders

This helps to clarify the intent of the feature and to communicate more clearly where the business outcome is aimed at.

Often this anti-pattern is further compounded when we write stories whose primary beneficiary is not the end user at all but some business stakeholder. Take the following case: the marketing department wish to sign users up for some third-party company marketing lists. Typically the user story would look like this:

As a user I want to be notified of special offers by carefully selected third-party companies

However, it's rarely the case that the user is driving this need, and the language somehow feels forced. In this case it's our marketing department who are driving this need, so it would explain the scenario's intent better and be far more honest, to rewrite this as

As the marketing director I want to collect customer email addresses to sell to third-party companies

Now when we read this story we can see where the requirement is coming from, and what the expected business outcomes are, which is what BDD is intended to accomplish.

2. 'When i fill in 'input.flooble-widgets[:first]' with 'FooBar'

If you're executing your feature files as part of an automated test suite (e.g. in a continuous integration environment), you'll be using a tool such as Behat, Cucumber or the like to drive a web browser to determine whether a particular feature has been completed or not. It's paramount to write the BDD in the domain language of your business[1], and unless your client is in the business of e.g. building online forms you should never see any reference to specific DOM elements (or arguably even field input values) in your features. Features should capture acceptance criteria for specific business outcomes, not merely be a higher level scripting language for Selenium, so always produce step definitions like

When I subscribe to the newsletter

rather than

And I set the value of 'input#newsletter-signup-checkbox' to '1'

and

When I search for articles containing the words 'rockets'

rather than

When I fill in 'input#search-terms' with 'rockets'
And I click 'button#search-button'

The actual implementation of the step definition should be encapsulated using the Page or Element context pattern[2] so that you're able to make any changes to the implementation as circumstances dictate (e.g. or if you need to ensure the requirement still works on a redesigned page with different DOM ids). Your features should always be written in the domain language of the business to ensure clarity of intent and that both technical and non-technical stakeholders are able to fully understand them.

3. 'And I wait 10 seconds' 

Some features you're testing (e.g. those in systems that rely on third-party systems with a unstable network latency, or in javascript-heavy pages) might exhibit delays in moving from one state to another preventing further steps from carried out. e.g. you might have to wait on an AJAX call to complete before a 'continue' button is enabled. A common anti-pattern often used to obviate this, and related to the scripting anti-pattern above, is to add a timeout to the feature. 

The main problem with this is that whilst it's written in something approaching domain language, it's unlikely to be an actual requirement. Your client is very unlikely to actually want their user to wait for ten seconds whilst some unrelated action occurs, so steps like these which exist to ensure that non-functional technical issues don't stop functional tests from executing should be rolled up into page element contexts, hiding the implementation of these workarounds.

So that's it - three common antipatterns to watch out for when writing your BDD features - hopefully they're of some help!

[1] http://domainlanguage.com
[2] http://extensions.behat.org/page-object

I'm Andrew - a Tech Architect and Agile Delivery Consultant. Find out more about the services I offer here