I’ve been using SpecFlow pretty regularly for a few weeks now, and I must say, I’m a fan. I find that it’s a lot easier to do test-first development because I’m writing the test in human-readable, business language.
One of the ideas that I didn’t understand right away was how to re-use generated step definitions across features and scenarios. ScenarioContext and FeatureContext give you great options to handle this, though. Let’s check out an example using a modified version of the default scenario SpecFlow generates with a new feature file:
Scenario: Add two numbers
Given I enter 50 into the calculator
And I press plus
And I enter 70 into the calculator
When I press enter
Then the result should be 120 be displayed
When I generate step definitions, I might end up with a class that looks like this:
namespace adamprescott.net.Calculator.SpecFlow
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TechTalk.SpecFlow;
[Binding]
public class Calculator_AddSteps
{
Calculator calculator = new Calculator();
[Given(@"I enter (.*) into the calculator")]
public void GivenIEnterIntoTheCalculator(int p0)
{
calculator.Number(p0);
}
[Given(@"I press plus")]
public void GivenIPressPlus()
{
calculator.Plus();
}
[When(@"I press enter")]
public void WhenIPressEnter()
{
calculator.Enter();
}
[Then(@"the result should be (.*) be displayed")]
public void ThenTheResultShouldBeBeDisplayed(int p0)
{
Assert.AreEqual(Convert.ToString(p0), calculator.Display);
}
}
}
Okay, not bad. A logical next feature might be subtraction. Some of the steps, like entering numbers and pressing enter, are shared. It would be nice if we could re-use those, but they’re configured to manipulate private variables in the Calculator_AddSteps class. So let’s do some refactoring! Instead of using a member-level variable, I can store my Calculator object in the ScenarioContext, making it accessible to other steps being executed in the same scenario.
// store to ScenarioContext like this:
ScenarioContext.Current.Set<Calculator>(new Calculator());
ScenarioContext.Current.Set<Calculator>(new Calculator(), "Calc");
ScenarioContext.Current["Calc"] = new Calculator();
// retrieve from ScenarioContext like this:
var c = ScenarioContext.Current.Get<Calculator>();
var c = ScenarioContext.Current.Get<Calculator>("Calc");
var c = ScenarioContext.Current["Calc"] as Calculator;
This is overkill for such a simple example, but I separated my shared steps into a new step definitions file. The final solution has three classes for the step definitions: Calculator_AddSteps, Calculator_SubtractSteps, and Calculator_SharedSteps.
Here’s the final solution, broken up by file:
Calculator_Add.feature
Feature: Calculator_Add
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Background:
Given I have a calculator
Scenario: Add two numbers
Given I enter 50 into the calculator
And I press plus
And I enter 70 into the calculator
When I press enter
Then the result should be 120 be displayed
Calculator_AddSteps.cs
namespace adamprescott.net.Calculator.SpecFlow
{
using TechTalk.SpecFlow;
[Binding]
public class Calculator_AddSteps
{
[Given(@"I press plus")]
public void GivenIPressPlus()
{
ScenarioContext.Current.Get<Calculator>().Plus();
}
}
}
Calculator_SharedSteps.cs
namespace adamprescott.net.Calculator.SpecFlow
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using TechTalk.SpecFlow;
[Binding]
public class Calculator_SharedSteps
{
[Given(@"I have a calculator")]
public void GivenIHaveACalculator()
{
ScenarioContext.Current.Set<Calculator>(new Calculator());
}
[Given(@"I enter (.*) into the calculator")]
public void GivenIEnterIntoTheCalculator(int p0)
{
ScenarioContext.Current.Get<Calculator>().Number(p0);
}
[When(@"I press enter")]
public void WhenIPressEnter()
{
ScenarioContext.Current.Get<Calculator>().Enter();
}
[Then(@"the result should be (.*) be displayed")]
public void ThenTheResultShouldBeBeDisplayed(int p0)
{
Assert.AreEqual(Convert.ToString(p0), ScenarioContext.Current.Get<Calculator>().Display);
}
}
}
Calculator_Subtract.feature
Feature: Calculator_Subtract
In order to avoid silly mistakes
As a math idiot
I want to be told the difference between two numbers
Background:
Given I have a calculator
Scenario: Subtract two numbers
Given I enter 70 into the calculator
And I press minus
And I enter 50 into the calculator
When I press enter
Then the result should be 20 be displayed
Calculator_SubtractSteps.cs
namespace adamprescott.net.Calculator.SpecFlow
{
using TechTalk.SpecFlow;
[Binding]
public class Calculator_SubtractSteps
{
[Given(@"I press minus")]
public void GivenIPressMinus()
{
ScenarioContext.Current.Get<Calculator>().Minus();
}
}
}
Like this:
Like Loading...