Spec Flow and Friends

I started to write a blog post about how I currently go about writing software and what Behaviour Driven Development (BDD) is to me, I got virtually to the end of the first draft, when it occurred to me that I was describing the use of tooling but had not described what the tools actually were! hence this post.

In addition to Visual Studio I am currently using:

    • 1.  Resharper which I have gone on about at some length at various times.
    • 2.  SpecFlow a plain text business readable specification test framework, and the subject of this post 
    • 3.  Machine Specifications a context unit specification framework, which I will cover next time.

SpecFlow

Based on the ruby framework Cucumber, SpecFlow takes a plain text feature document with a simple syntax and creates executable specifications for either the MSTest or the NUnit test runners. 

A feature file is almost the same as a user story within the agile context, a feature has a simple text description, and a number of structured scenarios each containing a number of steps that describe the setup execution and expected results in a ubiquitous language of the application, for example

Feature: Addition
    In order to avoid silly mistakes
    As a math idiot
    I want to be told the sum of two numbers

Scenario: Add two numbers
    Given I have entered 50 into the calculator
    And I have entered 70 into the calculator
    When I press add
    Then the result should be 120 on the screen

 

The only words required by SpecFlow in this whole feature are the ones in blue, everything else is up to the user, this allows for very specific business domain language to be used and because of the terseness of the required syntax, there is also support for other languages not only English. 

Looking at each of the SpecFlow syntax items in turn.

Feature:

the title of the feature is an important component of the system, this will directly translate into the class name of the generated test, so feature titles must be unique, the remainder of the feature text (after the carriage return) can be styled like the above example using the Connextra format

In order to [benefit]

As a [stakeholder]

I want to [feature]

or simply as a paragraph or more of description, or any other format the user wants to use, after this narrative comes one or more Scenarios which are the heart of the spec flow system.

Scenario:

There can be one or more scenarios for a given feature, these describe how the user interacts with a specific feature using a structured syntax

Given sets up the context for the scenario

When is the action that will create the result

Then asserts that the results created by the action are correct

The And keyword can be used in conjunction with any of the three scenario steps to extend them in a language friendly fashion.

The next stage of the process is to create the step definitions which link our text feature file to the production code.  A step definition is simply a class with the binding attribute and a method with the given, when or then attribute attached.  The method attribute is the binding that links the method to a step in a feature file.

  1. [Binding]
  2. public class StepDefinitions
  3. {
  4.     [Given(@"I have entered 50 into the calculator")]
  5.     public void GivenIHaveEntered50IntoTheCalculator()
  6.     {
  7.         ScenarioContext.Current.Pending();
  8.     }
  9.  
  10.     [Given(@"I have entered 70 into the calculator")]
  11.     public void GivenIHaveEntered70IntoTheCalculator()
  12.     {
  13.         ScenarioContext.Current.Pending();
  14.     }
  15.  
  16.     [When(@"I press add")]
  17.     public void WhenIPressAdd()
  18.     {
  19.         ScenarioContext.Current.Pending();
  20.     }
  21.  
  22.     [Then(@"the result should be 120 on the screen")]
  23.     public void ThenTheResultShouldBe120OnTheScreen()
  24.     {
  25.         ScenarioContext.Current.Pending();
  26.     }
  27. }

 

I actually copied these definitions from the NUnit text output window, when the specifications are run without definitions, the test runner outputs example steps for the developer to copy. Notice how the “And” has now become a “Given”, the ‘And’ keyword becomes the parent ‘Given’, ‘When’ or ‘Then’, depending on where it is used.  The text written in the scenario becomes the attribute value within the step definition  

Having a single step definition  for each step of the scenario is wasteful, making maintenance difficult and doesn’t comply with the DRY principle, the attribute text of the two ‘Given’ methods are nearly the same, the only difference is the entered values, we can combine these two steps into a single code step using a simple regular expression.

  1. [Given(@"I have entered (.*) into the calculator")]
  2. public void GivenIHaveEntered(int enteredValue)
  3. {
  4.     ScenarioContext.Current.Pending();
  5. }

 

this single given step now works for both the ‘Given’ and the ‘And’ steps in the scenario, the text in the feature is automatically converted in to an ‘int’ for us, ready for the for use within the code.

I can do the same with the ‘Then’ step, so that I can read the expected result.

  1. [Then(@"the result should be (.*) on the screen")]
  2. public void ThenTheResultShouldBe(int expectedResult)
  3. {
  4.     ScenarioContext.Current.Pending();
  5. }

 

At this point NUnit reports one inconclusive test and no missing step definitions and I am ready to start coding. 

Before I continue though I want to share some guidelines I have developed over time with regard to writing the feature text, and scenarios.

Getting the feature text right, before writing any step definitions is well worth the effort, it is a lot easier to edit the text before the step definitions are written, rather than trying to edit both sets of files later.

1. I use the narrative section of the feature file to describe the application and what this particular feature file is going to cover, all very high level and non technical,  this will be my application documentation in the future so I want it to be expressive and clear as to what each feature covers.

2. I get all the feature files written even if only in draft form, before writing any step definitions. these features define the application, and having the overall view helps guide the development process, helps remove duplication and can help guide the creation of the step definitions.

3. Once I have the feature text complete, I look at the sentence structure to see if there are places I can make them generic, I am thinking about the step definition process at this point, I want to reduce the number of steps to as few as possible, whilst maintaining the scenario uniqueness’s, if you have more than one feature file complete I try to look across features to reduce the step definition count.

Keeping the step definition count to as low as possible makes maintenance easier, because these tests cover a large part of the application they can become complex very easily, this must be managed, so that future maintenance is as easy as possible, I use the normal clean code practices to keeping methods small, with a single responsibility.

Coding the steps is now fairly easy, we only write the test code at this point, and only just enough production code to enable the application to compile.

  1. [Binding]
  2. public class StepDefinitions
  3. {
  4.     Calculator _calculator;
  5.     int _result;
  6.  
  7.     [BeforeScenario()]
  8.     public void setup()
  9.     {
  10.         _calculator = new Calculator();
  11.     }
  12.  
  13.     [Given(@"I have entered (.*) into the calculator")]
  14.     public void GivenIHaveEntered(int enteredValue)
  15.     {
  16.         _calculator.Number(enteredValue);
  17.     }
  18.     
  19.     [When(@"I press add")]
  20.     public void WhenIPressAdd()
  21.     {
  22.         _result = _calculator.Add();
  23.     }
  24.  
  25.     [Then(@"the result should be (.*) on the screen")]
  26.     public void ThenTheResultShouldBe(int expectedResult)
  27.     {
  28.         Assert.AreEqual(expectedResult, _result);
  29.     }
  30. }
  31.  
  32. public class Calculator
  33. {
  34.     public void Number(int enteredValue)
  35.     {
  36.         throw new NotImplementedException();
  37.     }
  38.  
  39.     public int Add()
  40.     {
  41.         throw new NotImplementedException();
  42.     }
  43. }

 

Notice how at this point my production code is actually in the test code base, I can move it easily later using Resharpers Move_to command. Another thing to notice is that I use field variables to store state between the various scenario steps, SpecFlow does contain a context bag which you can use to store state, I just prefer at the moment to use something I can control.

This test of cause actually fails at the moment with a not implemented exception which is what I expect. Now I have some choices,

If the code required to make the test is going to be complex then I will drop down into Machine Specifications, using the scenario as my requirement guide, and write TDD style tests to drive the required functionality, more of that in the next post!

But as in this case the code required is simple to implement or is simply calling other methods on already written classes, (A typical case for web page code) then I will simply write that code here, make the feature pass, and then move the passing production code up to the production project. 

In some cases it may be a combination of the two processes that complete the whole feature, the idea is to use the best tooling to get the job done,

  1. public class Calculator
  2. {
  3.     readonly IList<int> numbers = new List<int>();
  4.  
  5.     public void Number(int enteredValue)
  6.     {
  7.         numbers.Add(enteredValue);
  8.     }
  9.  
  10.     public int Add()
  11.     {
  12.         return numbers.Sum();
  13.     }
  14. }

Because SpecFlow has such a light touch, and can be made so expressive, it is worth writing a simple feature file for most projects, using the feature to focus the mind on the problem at hand, which  makes the final code easier to write, and the feature file can act as the documentation for the project.

Next time a quick overview of Machine Specifications,

Advertisements

About Duncan Butler

Trying to be a very agile software developer, working in C# with Specflow, Nunit and Machine Specifications, and in the evening having fun with Ruby and Rails
This entry was posted in Uncategorized. Bookmark the permalink.

One Response to Spec Flow and Friends

  1. Sebastijan says:

    Hi,

    When do you use specflow and when do you use machine specs? Is there a difference (except the one when writing a code)? Do you think it makes sense to use both on a single project and what would be a criteria to use one or the other? Is one better or easier to use then the other? If is too much to answer, maybe my question would be good starting point for your next article.

    Kind regards,
    Sebastijan

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s