Tutorial – BDD and Dependency Injection in .Net (4)

As usual we are picking up from where we left off. In this brief post we will expand our SpecFlow knowledge by focusing on parameterised the step definitions.

Currently the SpecFlow tests are as follows:

PlayRoundFeature.feature

Feature: Play a single round of Rock Paper Scissors
	As a player
	I want to play a round
	So that I can test my luck

Scenario: Computer chooses rock and the player chooses paper
	Given the computer makes a secret choice of rock
	When I choose paper
	Then the result should be “Player Wins!”

Scenario: Computer chooses rock and the player chooses scissors
	Given the computer makes a secret choice of rock
	When I choose scissors
	Then the result should be “Computer Wins!”

PlayRoundSteps.cs

namespace RockPaperScissorsTest.Specs.Steps
{
    [Binding]
    public class PlayRoundSteps
    {
        private const string GameKey = "Game";

        [Given(@"the computer makes a secret choice of rock")]
        public void GivenTheComputerMakesASecretChoiceOfRock()
        {
            var game = new Game(new DescissionEngine());
            ScenarioContext.Current.Add(GameKey, game);
        }

        [When(@"I choose paper")]
        public void WhenIChoosePaper()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            game.PlayerMove = "Paper";
        }

        [When(@"I choose scissors")]
        public void WhenIChooseScissors()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            game.PlayerMove = "Scissors";
        }

        [Then(@"the result should be “Player Wins!”")]
        public void ThenTheResultShouldBePlayerWins()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            Assert.AreEqual("Player Wins!", game.Result());
        }

        [Then(@"the result should be “Computer Wins!”")]
        public void ThenTheResultShouldBeComputerWins()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            Assert.AreEqual("Computer Wins!", game.Result());
        }

    }
}

Firstly, we make small changes to the feature file. Simply upper case the ‘choices’.  This is to make parsing easier at a later stage.

Feature: Play a single round of Rock Paper Scissors
	As a player
	I want to play a round
	So that I can test my luck

Scenario: Computer chooses Rock and the player chooses Paper
	Given the computer makes a secret choice of Rock
	When I choose Paper
	Then the result should be "Player Wins!"

Scenario: Computer chooses Rock and the player chooses Scissors
	Given the computer makes a secret choice of Rock
	When I choose Scissors
	Then the result should be "Computer Wins!"

Make the required changes in the PlayRoundSteps.cs file e.g.

        [Given(@"the computer makes a secret choice of Rock")]
        public void GivenTheComputerMakesASecretChoiceOfRock()
        {
            var game = new Game(new DescissionEngine());
            ScenarioContext.Current.Add(GameKey, game);
        }

Look how similar the following two step definitions are:

        [When(@"I choose Paper")]
        public void WhenIChoosePaper()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            game.PlayerMove = "Paper";
        }

        [When(@"I choose Scissors")]
        public void WhenIChooseScissors()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            game.PlayerMove = "Scissors";
        }

We can parameterise these step definitions to make them more generic and re-usable. We can collapse them into a single step definition:

        [When(@"I choose (.*)")]
        public void WhenIChoosePaper(string choice)
        {
            var game = ScenarioContext.Current.Get(GameKey);
            game.PlayerMove = choice;
        }

SpecFlow will use regular expression grouping to extract a sub-string from the matching steps in the feature fie. The matching steps from the feature file are:

  • When I choose Paper
  • When I choose Scissors

The regex group will select ‘Paper’ (or ‘Scissors’) and pass that into the step definition via the ‘choice’ parameter. The ‘choice’ parameter is used to set the ‘player move’ on the game object. This way we have a single step definition that can handle multiple steps.

We can perform a similar re-factoring with the following step definitions:

        [Then(@"the result should be “Player Wins!”")]
        public void ThenTheResultShouldBePlayerWins()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            Assert.AreEqual("Player Wins!", game.Result());
        }

        [Then(@"the result should be “Computer Wins!”")]
        public void ThenTheResultShouldBeComputerWins()
        {
            var game = ScenarioContext.Current.Get(GameKey);
            Assert.AreEqual("Computer Wins!", game.Result());
        }

The above step definitions can be collapsed into the following single step definition:

        [Then(@"the result should be ""(.*)""")]
        public void ThenTheResultShouldBePlayerWins(string result)
        {
            var game = ScenarioContext.Current.Get(GameKey);
            Assert.AreEqual(result, game.Result());
        }

The complete PlayRoundSteps.cs

namespace RockPaperScissorsTest.Specs.Steps
{
    [Binding]
    public class PlayRoundSteps
    {
        private const string GameKey = "Game";

        [Given(@"the computer makes a secret choice of Rock")]
        public void GivenTheComputerMakesASecretChoiceOfRock()
        {
            var game = new Game(new DescissionEngine());
            ScenarioContext.Current.Add(GameKey, game);
        }

        [When(@"I choose (.*)")]
        public void WhenIChoosePaper(string choice)
        {
            var game = ScenarioContext.Current.Get(GameKey);
            game.PlayerMove = choice;
        }

        [Then(@"the result should be ""(.*)""")]
        public void ThenTheResultShouldBePlayerWins(string result)
        {
            var game = ScenarioContext.Current.Get(GameKey);
            Assert.AreEqual(result, game.Result());
        }

    }
}

We will explore more SpecFlow features as we progress with the tutorial.

2 thoughts on “Tutorial – BDD and Dependency Injection in .Net (4)

Leave a Reply

Your email address will not be published. Required fields are marked *