Chapter 6

Making Scenarios More Expressive

Chapter 3, “Formalizing Examples into Scenarios,” covered how to use Cucumber’s Gherkin language to express examples of your app’s behavior. In this chapter, we look at how to go beyond the basics to create scenarios that are fluent, expressive, and a pleasure to read and write. We’ll see some common ways scenarios go wrong and how to fix them.

Feedback About Scenarios

As is often the case with teams new to Cucumber, the library team did reasonably well when working on their first feature or two, but they began to struggle with the expressiveness of their scenarios as they moved to a new area of their domain and involved new team members in writing scenarios. From their initial time with Jonah, they had good reference examples around the book search behavior. Now, they have some less-than-expressive scenarios and reengage Jonah to help them improve.

How to Make Your Scenarios More Expressive

In his book Peak, psychologist Anders Ericsson points to the importance of powerful mental representations of a domain as a key to expert performance. Expert chess players, for example, think about the positions of the pieces on a chess board differently from novices. The experts see combinations of positions, relative power, lines of force, where the novices see only individual pieces in arbitrary places. This allows the experts to reason more powerfully about where to move next.

Expressive Cucumber scenarios help developers and stakeholders build a mental representation of how the system is intended to behave. The better the representation, the better decisions they’ll be able to make about what feature to build next, how to perform exploratory testing, and how to avoid defects, among other things.

Poorly written scenarios can still function as automated tests. They keep you from unknowingly breaking something in the system that used to work, but they squander an opportunity to use scenarios to produce better and better software.

To get expressive scenarios, we need to look at three things: the level of abstraction, the appropriate amount of detail, and the language in the steps themselves.

Finding the Right Level of Abstraction

The same scenario can be described using several different kinds of language. In the previous dialogue, the team wrote scenarios at two extremes. Their first scenario, the version written by Jane, was too low-level. It included implementation details and read like a manual test script. When Sam tried to fix it, he went too far the other way and created what we call a tautological scenario. It abstracted so many details away that it no longer expressed anything meaningful about the behavior (other than telling us that successful behavior should be considered successful).

Avoiding Tautological Scenarios

At one extreme is the tautological scenario, with insufficient detail to illustrate the feature. Sometimes these aren’t even a proper example. Clues that indicate a tautological scenario are words in the Then step like correct or successful, as in the following example:

Scenario: Search for a book 
  When I search for a book
  Then I should see the correct results

When you find yourself writing a tautological scenario, ask yourself, “For example?” After you generate one example, ask, “What’s another example?” Tautological scenarios often hide several examples that illustrate different aspects of what correct or successful mean. It can also be useful to explicitly define terms, as in, “How would I know if I had correct results?” and “What would incorrect results look like?”

Avoiding Overly Technical Scenarios

At the other extreme is the technical scenario, containing details meaningful only to technical team members and tightly coupled to a particular implementation. For example, this scenario has XPath and CSS selectors and two database tables related by numeric key:

Scenario: Search for a book by author
  Given the following Authors:
    | id | name               |
    | 1  | Charles Dickens    |
    | 2  | John Steinbeck     |
    | 3  | Fyodor Dostoyevsky |
  And the following Titles:
    | id | title                | author_id |
    | 1  | A Tale of Two Cities | 1         |
    | 2  | Crime and Punishment | 3         |
    | 3  | East of Eden         | 2         |
  And I'm on "http://www.bookstore.biz/"
  When I fill in "Dickens" in "//input[name()='q']"
  And I click "#searchButton"
  Then there should be 1 "div.book div.title" element
  And the page should contain "A Tale of Two Cities"

Slightly more readable, but still far from a ubiquitous language, is the scenario written in implementation language like the following scenario. This kind of scenario tends to have words and phrases like click, fill in, select, page, and screen. Like the technical scenario, these usually are imperative, describing the actions one would take to test the scenario rather than the goals these actions support in terms of the business domain.

Scenario: Search for a book by author
  Given the following catalog:
    | Title                | Author             |
    | A Tale of Two Cities | Charles Dickens    |
    | Crime and Punishment | Fyodor Dostoyevsky |
    | East of Eden         | John Steinbeck     |
  And I'm on the bookstore home page
  When I fill in "Dickens" in the search field
  And I click "Search"
  Then I should see only "A Tale of Two Cities" in the results
The Sweet Spot: A Concrete Domain Example

The most useful Cucumber scenarios express a concrete domain example using the language of the domain, supporting what Eric Evans calls a ubiquitous language in Domain-Driven Design. Ubiquitous language is the idea that the same language should be used across the whole software development team, from conversations between business and technical people to tests to the code itself. There shouldn’t be translation layers of domain concepts between business and technical representations. Growing a ubiquitous language involves effort and commitment but enables a team to have more effective conversations and enables the software to become increasingly expressive of domain concepts. This increase in expressiveness makes the code easier to understand and thus more amenable to change.

Leslie Brooks puts it well when he explains how ubiquitous language benefits his teams:

The mantra we use is, “If we mean the same thing, we should write it exactly the same way. If we write it the same way, we should mean exactly the same thing.” That gives the developers confidence—if they see the same words, then they know that they know exactly what it means. “I have seen that phrase before. I know what it means, and I can reuse the code that I wrote to implement it.” If one word is different, then they know that one-word difference is significant, and they must understand the difference before they implement it.1

Ubiquitous language is different from the idea that technical team members should simply adopt the terms used by domain experts. In many cases, domain experts have never had to discuss their domain with the precision required to represent domain concepts in software. This need for precision reveals inconsistencies and ambiguities in the jargon of the domain. Thus, a particular team will need to grow its own ubiquitous language. Many elements of the language will come from existing jargon, but others will emerge from conversation within the team.

A concrete domain example for a book search might look like this:

Scenario: Search for a book by author
  Given the following catalog:
    | Title                | Author             |
    | A Tale of Two Cities | Charles Dickens    |
    | Crime and Punishment | Fyodor Dostoyevsky |
    | East of Eden         | John Steinbeck     |
  When I search for "Dickens"
  Then I should see only the following in the results:
    | Title                | Author          |
    | A Tale of Two Cities | Charles Dickens |

This change avoids implementation and technical detail and combines individual actions the user might take on a web page into larger tasks that express meaningful goals in the business domain. There’s a nice symmetry between the Given and Then steps, as well, which highlights the behavior of the search operation.

Including the Appropriate Details

Some scenarios are written in good domain language and describe a concrete example but contain excess detail. A large number of quoted strings, numeric values, or table columns can indicate excess detail in a scenario. For example:

Scenario: Search for a book
  Given the following catalog:
    | Title                | Author             | Publisher | ISBN       |
    | A Tale of Two Cities | Charles Dickens    | Qualitas  | 1897093594 |
    | Crime and Punishment | Fyodor Dostoyevsky | Dover     | 1936041030 |
    | East of Eden         | John Steinbeck     | Penguin   | 0142000655 |
  When I search for "Dickens"
  Then I should see only the following in the results:
    | Title                | Author          | Format    | Price  |
    | A Tale of Two Cities | Charles Dickens | Paperback | $14.90 |

To fix this kind of scenario, challenge the details: “Is it really necessary to know the publisher or ISBN for this example?” Try removing details to see if the scenario still works. Does it still express the example well? Does it differentiate the example from other similar examples?

Exactly what detail is relevant is highly context-specific. Remove too much detail, and you get a tautological scenario that doesn’t really illustrate the behavior. The reader needs to bring too much to the table for the scenario to usefully support the mental representation. Leave excess detail in, and the scenario is noisy, requiring the reader to parse out what’s important and what’s incidental. The best approach is to experiment, to try multiple variations, until you’ve hit the sweet spot for that unique context.

Sometimes, excess detail gets into scenarios from a well-intentioned desire to increase reuse of step definitions. Reuse has value, but for scenarios, we strongly recommend prioritizing expressiveness over reuse. Use helper methods in your step definitions to increase reuse at that level, but allow for similar yet slightly different steps in your scenarios where it improves expressiveness.

Expressive Language in the Steps

Up to this point, we’ve talked about scenario language in terms of level of abstraction and amount of detail. The words and grammar you use in the steps themselves also affect your scenarios’ expressiveness.

Actors and Verb Tense

We like Given steps to be written in the passive voice. Given describes context, things that just are, so we shouldn’t express how they got that way. For example, instead of

Given Chandra Khan <[email protected]> signs up as a library patron
And Denise Jones <[email protected]> signs up as a library patron

we prefer

Given the following library patrons:
  | Name         | Email                    |
  | Chandra Khan | [email protected]   |
  | Denise Jones | [email protected] |

We shouldn’t care if they just signed up as patrons or if they’ve been patrons for years. What matters is simply that they’re already patrons one way or another by the time we hit the When step.

When steps are about action, so we write them in present tense in the active voice. Someone is doing something that’s going to change the state of the system somehow.

There’s debate in the Cucumber community about whether the action should be described in the third person (“When the patron does X”) or in the first person (“When I do X”). We almost always prefer the first person approach. One of the key functions of scenarios is to build empathy with users so we build products that meet their needs. What better way to empathize with a user than to explicitly put ourselves in her shoes in a scenario, seeing her interactions with the system from her perspective? So, instead of

Given Chandra Khan is a library patron
When she logs into her library account

we like

Given I'm library patron Chandra Khan
When I log into my library account

The former has us watching Chandra from the outside. The latter has us imagining what it’s like to be Chandra using the library system. It’s a subtle difference, but we’ve seen times where that shift causes a team to see something differently and build a better feature.

Be sure that the reader of your feature will be clear who the user is before you take on their voice. We usually establish that context with a step like Given I'm so and so in the Background of a feature or at the start of a scenario.

The one time where we prefer When steps to use the third person is when there are multiple actors and it needs to be clear who’s doing what. For example, in a chat application, many scenarios would involve two or more people interacting and could benefit from naming the actors throughout.

Finally, Then steps express an expectation, a preference, for how the state of the system will change after the action in the When step. To make that sense of expectation clear, we like to use the word should. Instead of

When I pay my outstanding fines in full
Then my account is reinstated

use

When I pay my outstanding fines in full
Then my account should be reinstated

Sometimes, the outcome in a Then step is one the user wouldn’t like. For example:

Scenario: Fines accrue daily
  Given the standard fine is $0.25 per day
  And I've borrowed a standard book
  When I return the book 4 days late
  Then I should be fined $1.00

No one really wants to be fined. But they do want their fines to be calculated correctly. So, we like the word expect to express expectation, if not preference. So, the Then step in the preceding scenario changes to

Then I expect to be fined $1.00

Along the lines of the first versus third person preference in When steps, some people feel strongly about keeping the actor out of the Then step, so they’d prefer

Then my account should be reinstated

to be changed to

Then the system should reinstate my account

We don’t feel strongly about this. If it’s more natural in English to express the expectation in terms of what the user sees, do so. If it’s more natural to talk about what the system does or how its state changes, do that instead.

Remove the Filler

For some reason, new Cucumber users tend to include filler words in their steps. The classic example is Given that when just Given would do the job. In the following step, that doesn’t do any work:

Given that I'm library patron Chandra Khan

We can simply delete it. Look for other words that don’t earn their space in a step—delete them to focus the reader on the core meaning.

To some, this level of fine-tuning feels excessive. But consider this: Every step will be read many more times than it’s written. It’s worth a little extra care with our language at writing time to make the reading easier and more pleasant.

Entities and Attributes

We often see steps that look like this:

Given a library patron with name "Chandra Khan" and email "[email protected]"

This step is creating an entity with certain attributes. It’s reasonably expressive, but it’s long (and it’s only going to get longer as we add attributes). We prefer to express steps like this using a key-value table argument:

Given a library patron with:
  | Name  | Chandra Khan           |
  | Email | [email protected] |

This approach makes it easy for the reader to associate the kind of entity (a library patron) with the two pieces of data in this particular one. (It’s also easier to work with in the step definition.)

Notice the colon after with. Cucumber doesn’t require it, but it’s a small touch to make the step more readable for humans, telling the reader to look down to the next line.

Refactoring Scenarios

To improve a scenario written in technical or implementation language, ask, “How would a nontechnical user describe this step?” Consider, “If I implemented this with a completely different technology, what step would describe both implementations?” Look for steps that can be combined into a single domain action. For example, going to the home page, filling in the search field, and clicking the search button could all be considered part of the single action of searching for a book.

When faced with a technical scenario like the earlier example, it can sometimes be useful to refactor through a series of small changes to the language rather than directly to domain language. First, change steps like

When I fill in "Dickens" in "//input[name()='q']"

to

When I fill in "Dickens" in the search field

Then, once you have the noise of many of the technical details out of the way, it will be easier to identify the larger domain concepts in the scenario.

It’s not always obvious how best to represent a particular example as a Cucumber scenario. While there are some guidelines to follow (such as avoiding technical language), exactly what language is best is partly a matter of style and personal preference. Language style will vary by team and domain. Don’t be afraid to try multiple approaches and keep the one you like best. In a new domain or area of an application, we’ll often write the same scenario three different ways to see which is more expressive. (Note how we make this decision using concrete examples rather than theoretical arguments about which is best.)

Be careful, though, not to get paralyzed trying to make your features perfect. Your understanding of the domain is always changing and growing. Your skill with Cucumber is growing. And your system is changing every day. Your features will never be perfect, but you can certainly take care with them and make them better.

Good Scenario Titles

Good scenario titles contribute to the readability of a feature. A good scenario title answers the question, “What is this particular example intended to illustrate?” Usually, that’s a rule or a rule plus something about the particular variation illustrated by this example.

Remember that the scenario titles need to make sense in the context of the feature title. Don’t repeat the feature title in each scenario title.

So, instead of

Feature: Ebook Search
  Scenario: Search for a book

we’d want to see something like

Feature: Ebook Search
  Scenario: By author - Complete last name matches

or

Feature: Ebook Search
  Scenario: By author - Partial last name matches

These scenario titles express what we’re doing—searching by author—as well as the particular variation of author search this particular scenario illustrates.

Summary

  • Expressive Cucumber scenarios build shared understanding on a team and function as long-term living documentation. Scenarios are read many more times than they’re written. Getting the language right is worth spending time on.

  • Scenarios should describe concrete domain examples in domain language.

  • Avoid tautological scenarios, ones that are abstracted so far from the details of the system that they essentially say, “It’s successful when it’s successful.”

  • Avoid overly technical scenarios that contain technical implementation details.

  • Include details that are essential for a particular example. Strip out excess detail.

  • Use passive voice in Given steps. Write When steps in present tense in the active voice, usually in first person as the user of the system. Use words like should or expect in Then steps to communicate a preference or expectation for a certain outcome.

  • Strip out any filler words in your steps that don’t do work.

  • Use key-value tables in steps that create an entity.

  • Refactor scenarios in small steps. Try two or three alternatives to find the phrasing that works best (using concrete examples!), but don’t get hung up trying to make your scenarios perfect.

  • Give your scenarios good titles that express what behavior this particular example is illustrating.

References

Ericsson, Anders. Peak: Secrets from the New Science of Expertise. Boston: Houghton Mifflin Harcourt, 2016.

Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Boston: Addison Wesley, 2004.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.242.141