Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Agile Project Planning Using Effect Maps and Specification-by-Example - Part 2

5.00/5 (1 vote)
7 Jan 2016CPOL3 min read 6.4K  
The second of a 2 part series on effect mapping and specification by example as tools for project planning. This second part focuses on specifications.

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:

C#
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)