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.
- Open Visual Studio, and go to Tools > Extensions and Updates (Extension Manager in VS2010)
- Select the Online gallery
- Search for “SpecFlow”
- 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.
- In Visual Studio, create a new Unit Test Project
- 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>
-
- Add a SpecFlow Feature to your project
- Generate Step Definitions by right-clicking in your newly created feature file
- 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
- 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
- I added my class-to-be-tested to the same file as my step definitions for brevity
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.
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
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");
}