BDD in .NET with SpecFlow

Yesterday, I was invited to sit in on a demo/discussion about behavior-driven development (BDD) with SpecFlow. As a self-proclaimed unit test enthusiast, I was excited right away.

I’ve been doing my own, made-up version of BDD for a while. For example, I would have a function that calls several sub-methods. I’d mock the sub-methods and write tests to verify that the sub-methods were called. Then I’d move to the next “layer” by implementing the sub-methods in a similar fashion, repeating until I hit the bottom layer, where the actual work happens.

The problem with that approach is that all tested behaviors relate to the code and not business requirements or real-world scenarios. SpecFlow fixes that by using a Gherkin parser, allowing you to write test scenarios using business-readable, domain-specific language instead of code.

Here’s how you can get started with SpecFlow and Visual Studio. This tutorial is basically a modified version of the Project Setup Guide on the SpecFlow site. I ran into a couple issues with their tutorial, though, and their tutorial also doesn’t get much into implementing the application logic to fix unit tests once they’re generated.

Installing SpecFlow

SpecFlow is included in the Visual Studio Extensions Gallery, so installing it is a breeze.

  1. Open Visual Studio, and go to Tools > Extensions and Updates (Extension Manager in VS2010)
  2. Select the Online gallery
  3. Search for “SpecFlow”
  4. Install it!

Nice, now you’re ready to create your first test project.

Creating your first test project

SpecFlow tests are defined as “features” that have multiple scenarios. There is a small amount of project setup that needs to be done, but it’s not too bad and you can be up and running in just a few minutes.

  1. In Visual Studio, create a new Unit Test Project
  2. Install SpecFlow via NuGet
    • PM> Install-Package SpecFlow
    • I had to manually add the unit test provider to my app.config:
      <?xml version="1.0" encoding="utf-8"?>
      <configuration>
        <configSections>
          <section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow" />
        </configSections>
        <specFlow>
          <unitTestProvider name="MsTest" />
        </specFlow>
      </configuration>
      
  3. Add a SpecFlow Feature to your project
  4. Generate Step Definitions by right-clicking in your newly created feature file

  5. Run your scenario by right-clicking in the feature file

    • Right-clicking in the feature and choosing Run SpecFlow Scenarios produced an error for me; I’ll be looking into this
    • I instead ran the unit tests as usual, and that worked fine
  6. Implement your code/step definitions until you have a passing test
    • I added my class-to-be-tested to the same file as my step definitions for brevity
      namespace adamprescott.net.SpecFlow.FirstProject
      {
          using Microsoft.VisualStudio.TestTools.UnitTesting;
          using System.Collections.Generic;
          using System.Linq;
          using TechTalk.SpecFlow;
      
          public class AddingMaching
          {
              private List<int> _numbers = new List<int>();
              private int _sum;
      
              public int Screen
              {
                  get { return _sum; }
              }
      
              public void EnterNumber(int num)
              {
                  _numbers.Add(num);
              }
              public void PressAdd()
              {
                  _sum = _numbers.Sum();
              }
          }
      
          [Binding]
          public class MyFirstFeatureSteps
          {
              private AddingMaching _addingMachine;
      
              public MyFirstFeatureSteps()
              {
                  _addingMachine = new AddingMaching();
              }
      
              [Given(@"I have entered (.*) into the calculator")]
              public void GivenIHaveEnteredIntoTheCalculator(int p0)
              {
                  _addingMachine.EnterNumber(p0);
              }
      
              [When(@"I press add")]
              public void WhenIPressAdd()
              {
                  _addingMachine.PressAdd();
              }
      
              [Then(@"the result should be (.*) on the screen")]
              public void ThenTheResultShouldBeOnTheScreen(int p0)
              {
                  Assert.AreEqual(p0, _addingMachine.Screen);
              }
          }
      }
      
    • The example shows a simple AddingMachine class with implemented steps to pass the default feature scenario
      Feature: MyFirstFeature
      	In order to avoid silly mistakes
      	As a math idiot
      	I want to be told the sum of two numbers
      
      @mytag
      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
      

That’s it! Now you should have a passing unit test that verifies the business-language requirement. You can add more scenarios to your feature file with different values and/or steps. Note that if you add more steps, you’ll probably need to re-generate step definitions. When you do this, only new step definitions will be generated. I recommend using the Copy methods to clipboard button in the Generate Step Definitions dialog to avoid overwriting the previously created and implemented step definitions.

Advertisement

Author: Adam Prescott

I'm enthusiastic and passionate about creating intuitive, great-looking software. I strive to find the simplest solutions to complex problems, and I embrace agile principles and test-driven development.

6 thoughts on “BDD in .NET with SpecFlow”

  1. Thank you. It is very useful and interesting.
    But I wonder if I could write specFlow scenario with state (non-action) pre-conditions (given)? For example, how to write the Steps.cs for the following scenarios if they are correct:

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

    @mytag
    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

    Scenario: Add another number
    Given The result was 120 on the screen
    And I have pressed add
    When I enter 10 into the calculator
    Then the result should be 130 on the screen

    1. I believe you can do what you’re suggesting by defining a step that executes the other steps. See if this article helps: https://adamprescott.net/2012/10/24/calling-steps-from-steps-in-specflow/. Using the technique described, you can create a step called “The result was 120 on the screen” that executes the steps necessary to achieve that state, something like this:

      [Given(@"The result was 120 on the screen")]
      public void GivenTheResultWas120OnTheScreen()
      {
      Given("I have entered 50 into the calculator");
      Given("I have entered 70 into the calculator");
      Given("I press add");
      }

Leave a comment

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 )

Connecting to %s

%d bloggers like this: