Specifications are a set of business rules with accompanying examples that capture all the scenarios and meaningful edge cases that define how a piece of functionality should be implemented. They are written in a way that can be automated and continuously run against the code base during development, which serves to highlight any potential issues around shared understanding of the problem space.
In my previous post, I discussed creating an effect map to plan the high level functionality of a project. Once the effect map is created, you end up with a bunch of features and a rough priority order that you can use for planning and scheduling. The features themselves can be fleshed out in such a way as to provide the blueprint of how the system should work from a business perspective. To go back to our old friends, Acme Inc. and their robot pets, the team felt that new customers were the highest priority, as a pet of any description is for life and the robot pets may well outlive their owners (they should at least outlive their 12 month warranty). They decide to focus on browsing the catalogue first and are primed to write their first feature. Being a Microsoft house, they are using SpecFlow, but could have used other tools such as Cucumber. The underlying language (Gherkin) is the same for both. Firing up Visual Studio, they add PetSearchFeature.feature
to their acceptance test project.
A feature is basically a collection of scenarios with examples that capture the business requirements for a piece of functionality. They become in essence living documentation, a true representation of the current functional state of the system provided that they evolve as the system evolves. Whenever you write a scenario, SpecFlow searches the locations it knows about (the assembly with the feature in unless you tell it to look elsewhere) for classes decorated with the Binding
attribute, and from those looks for the method whose attribute matches the text in the scenario statement. As an example, here's Acme's pet search feature:
Feature: Search for pets
In order to narrow down my purchase options
As a site visitor
I want to be able to search the catalogue for my perfect robot pet
Background:
Given a pet catalog containing
| Size | Price | Type | Pet Breed |
| Small | 100 | Cat | Siamese |
| Small | 150 | Cat | Burmese |
| Small | 200 | Cat | Siamese |
| Large | 200 | Dog | Boxer |
| Large | 250 | Dog | Sheepdog |
| Large | 500 | Primate | Chimpanzee |
| Large | 550 | Primate | Gorilla |
Scenario: All search criteria are combined when searching
Given the following search criteria
| Max Price | Type | Breed |
| 150 | Cat | Siamese |
When all criteria are used to search the catalog
Then the results are filtered by price
And the results are filtered by type
And the results are filtered by breed
And the following pets are found
| Size | Price | Type | Pet Breed |
| Small | 100 | Cat | Siamese |
Scenario: Searching using Max Price
Given I search using a Maximum Price of 150
When the results are filtered by price
Then the following pets are found
| Size | Price | Type | Pet Breed |
| Small | 100 | Cat | Siamese |
| Small | 150 | Cat | Burmese |
Scenario: Searching using Min Price
Given I search using a Minimum Price of 150
When the results are filtered by price
Then the following pets are found
| Size | Price | Type | Pet Breed |
| Small | 150 | Cat | Burmese |
| Small | 200 | Cat | Siamese |
| Large | 200 | Dog | Boxer |
| Large | 250 | Dog | Sheepdog |
| Large | 500 | Primate | Chimpanzee |
| Large | 550 | Primate | Gorilla |
Scenario: Searching using Pet Type
Given I search using a type of Dog
When the results are filtered by type
Then the following pets are found
| Size | Price | Type | Pet Breed |
| Large | 200 | Dog | Boxer |
| Large | 250 | Dog | Sheepdog |
Scenario: Searching using Pet Breed
Given I search using a breed of Gorilla
When the results are filtered by breed
Then the following pets are found
| Size | Price | Type | Pet Breed |
| Large | 550 | Primate | Gorilla |
The examples enable you to concisely capture all the edge cases that you need to consider without the needless repetition of creating a scenario per edge case. If you uncover further edge cases as development progresses, it is relatively simple to capture them and ensure they are tested.
Once the scenarios have been created, you can run them in your unit test runner of choice and SpecFlow will mark each of them as inconclusive because it cannot find any implementation code that matched the scenarios. Handily, SpecFlow also provides you the developer with a handy code snippet that you can copy and paste, which gives you your implementation method structure. SpecFlow works by scanning the assemblies it knows about for classes marked with a Binding
attribute. Once it finds those, it looks for a method marked with either a Given
, When
or Then
attribute that has a parameter matching the text of the scenario. The code for this statement: "Given I search using a Minimum Price of 150
" might look something like this:
namespace Acme.RobotPets.Web.Test.Acceptance
{
using TechTalk.SpecFlow;
[Binding]
public class PetSearchSteps
{
[Given(@"I search using a Minimum Price of (.*)")]
public void GivenISearchUsingAMinimumPriceOf(int price)
{
ScenarioContext.Current.Pending();
}
}
You are then free to start writing your implementation code, which is executed by the SpecFlow framework in much the same way as standard unit test code is executed. As such, it can be brought into a continuous integration process and become a first class project deliverable.
View the original article.