In this series of five articles, I want to help you get started with using SpecFlow in your test automation project. In this chapter, we’ll take a close look at creating more intelligent and flexible scenarios with the goal of creating expressive specifications that support communication about application behaviour and acceptance test goals and results.
Tutorial Chapters
- BDD, SpecFlow and The SpecFlow Ecosystem (Chapter 1)
- Getting Started with SpecFlow (Chapter 2)
- Writing More Expressive SpecFlow Steps (Chapter 3)
- You’re here → Tidying Up Your SpecFlow Features and Scenarios (Chapter 4)
- Working with SpecFlow Tables and SpecFlow.Assist (Chapter 5)
In the previous article, you’ve seen a number of ways to make individual steps in SpecFlow more expressive. This helps you create steps and scenarios that are highly readable and close to the business domain language that is spoken in your organization.
In this article, we’re going to look at some other techniques that help you clean up your scenarios and feature files. As your SpecFlow project grows in size, you might find yourself repeating specific scenarios, or parts of it, as you add more feature files. The techniques demonstrated in this article will help you keep your specifications organized and well-structured without losing expressive power.
Cleaning Up Repeated Scenarios with Scenario Outline
Especially when you’re working with scenarios that involve calculations, algorithms or other types or business logic revolving around combinations of data, you will likely end up with various iterations of the same scenario, describing the same underlying logic, but with different combinations of input and output values.
This is an even more likely outcome when you’re using techniques like Example Mapping (https://cucumber.io/blog/example-mapping-introduction/) to come up with descriptive and useful examples for the behaviour that you’re trying to specify, develop and/or verify.
However, repeating the same scenario, i.e., the same business logic, over and over again, just with different data, quickly becomes tedious to read and maintain. Consider these three example scenarios for our Zippopotam.us API, where we request location data for three different combinations of country and zip code and check that the location data returned contains a specific place name:
Scenario: Country code us and zip code 90210 yields Beverly Hills Given the country code us and zip code 90210 When I request the locations corresponding to these codes Then the response contains the place name Beverly Hills Scenario: Country code fi and zip code 99999 yields Korvatunturi Given the country code fi and zip code 99999 When I request the locations corresponding to these codes Then the response contains the place name Korvatunturi Scenario: Country code ca and zip code B2A yields North Sydney South Central Given the country code ca and zip code B2A When I request the locations corresponding to these codes Then the response contains the place name North Sydney South Central
You can see how this quickly becomes tedious to read, as well as a burden to maintain.
To help deal with this problem, SpecFlow offers the Scenario Outline, a method to define templates for scenarios containing placeholders where the actual input and output values go, followed by Examples containing the actual values for each iteration or scenario.
Here’s what our scenarios look like after being transformed into a Scenario Outline with Examples:
Scenario Outline: Country code and zip code combinations yield the expected place names Given the country code <countryCode> and zip code <zipCode> When I request the locations corresponding to these codes Then the response contains the place name <expectedPlaceName> Examples: | countryCode | zipCode | expectedPlaceName | | us | 90210 | Beverly Hills | | fi | 99999 | Korvatunturi | | ca | B2A | North Sydney South Central |
Much better! 👌 As you can see, our Scenario Outline contains placeholders for the country code, zip code and expected place name values (<countryCode>
, <zipCode>
and <expectedPlaceName>
, respectively), which are substituted by SpecFlow with the corresponding values in the Examples table.
When we run this Scenario Outline, SpecFlow translates it into three iterations that all invoke the same logic, just with different combinations of data, which is exactly what we wanted to achieve:
⚠ One warning: if you look closely at the output given by Visual Studio, you see that the examples are not run in the order in which they are specified (in this case, the underlying NUnit unit testing framework runs them in alphabetical order of the first parameter value). This should not be a problem as long as you make sure that your tests or iterations do not depend on one another, which is a good thing to practice in test automation in general.
Extracting Common Setup Steps in a Background
Another situation where you might run into step duplication and unnecessarily verbose feature files is when you have different scenarios that all require the same initial state. Consider for example these three scenarios:
Scenario: An existing country and zip code yields the correct place name Given the country code us and zip code 90210 When I request the locations corresponding to these codes Then the response contains the place name Beverly Hills Scenario: An existing country and zip code yields the right number of results Given the country code us and zip code 90210 When I request the locations corresponding to these codes Then the response contains exactly 1 location Scenario: An existing country and zip code yields the right HTTP status code Given the country code us and zip code 90210 When I request the locations corresponding to these codes Then the response has status code 200
All three scenarios in this example share the same Given step, indicating that all three scenarios require the same initial state. While the example here is relatively brief, this can get difficult to read quickly for longer scenarios and feature files with more scenarios.
To circumvent this, SpecFlow offers the possibility to move these setup steps to a Background section, to be placed before the first scenario in your feature file. It will automatically run the steps in the Background section before each scenario in that feature file.
When we extract the common setup steps into a Background section, our feature file now looks like this:
Background: Specify country and zip code Given the country code us and zip code 90210 Scenario: An existing country and zip code yields the correct place name When I request the locations corresponding to these codes Then the response contains the place name Beverly Hills Scenario: An existing country and zip code yields the right number of results When I request the locations corresponding to these codes Then the response contains exactly 1 location Scenario: An existing country and zip code yields the right HTTP status code When I request the locations corresponding to these codes Then the response has status code 200
Running our scenarios shows us that these work in exactly the same way as before we moved the Given step into the Background section. Making this work also requires no additional changes to the step definition code.
You might have noticed that in this specific example, you could have also included the When step in the Background section. While this is technically possible in SpecFlow, it is considered good practice to only include steps that describe a required initial state in the Background. Including steps that describe the action to be undertaken (‘When’) or even assertions to be made (‘Then’) will likely result in loss of understanding with the reader as to what behaviour the scenario exactly describes.
Other Considerations
Next to the two techniques described above, there are several other guidelines that can help you make your steps and scenarios more expressive and increase their readability:
- Use of perspective and tense. To make your scenarios come alive, use the third-person perspective and the present tense as much as possible. ‘Given Anna is a first-time user of the system’ is much easier on the eyes than ‘Given I have used the system 0 times’.
- Only include test data that influences the outcome of the scenario. If you’re unsure as to how much detail you should go into when you’re including input variables into your scenarios, think hard about whether or not the value that a test data value takes on is important for the outcome of the scenario. Is it significant for this scenario? Then express it. Doesn’t really matter? Take care of it in the underlying step definition code. Remember, Gherkin scenarios are meant to be readable, not data-complete. Don’t be the person that specifies 137 data columns in a Gherkin scenario only because all 137 columns in a database table need a value (how I wish I made this scenario up…).
There are many other tips and tricks I could give you that help you make scenarios easier to read. Instead of listing them all here, I’d like to refer you to this blog post by Andrew Knight and this one by Thomas Sundberg for many more good tips on how to write effective Gherkin.
In the final article in this series (coming up next week!), we’re going to take a look at how you can effectively work with data tables in SpecFlow in order to work with more complex data structures as part of your Gherkin steps.
The example project used in this article can be found on GitHub: https://github.com/basdijkstra/testproject-specflow.