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

In part 2 of this tutorial, we moved the decision logic out of the Game class into the Decision Engine class to separate concerns. Now we introduce dependency injection.

In this part of the tutorial, we will use dependency injection (DI) to improve the design of the code base. Zby provides a useful definition of dependency injection: “Dependency injection means giving an object its instance variables instead of letting it create them”.

In our case, we will be giving the Game object an instance of Decision Engine instead of letting the Game object create it itself. We will do this via the Game’s constructor, however it could be done though a property or a method. These alternatives are known as constructor injection, property injection and method injection respectively.

This is the essence of the dependency injection pattern. Confusion tends to creep in when a dependency injection container is introduced. DI containers, such as unity or Castle Windsor, provide mechanisms to aid in implementing the dependency injection pattern. However, they are not required to implement the pattern. I recommend the use of DI containers when the benefit of using one outweighs the complexity that is introduced by using it.

For the sake of simplicity, I will be using the dependency injection pattern and will avoid dependency injection containers for the time being. Returning to the rock, paper scissors example, DI will provide two main benefits:

  • Flexibility – we will be able to provide different implementations of the Decision Engine without changing the Game class. This ‘pluggable architecture’ adheres to the open close principle. An example of this would be a decision engine that not only decides on a result, but also logs the results to a data store. I will return to this should time allow.
  • Testability – during testing, we will be able to pass a mock object to the Game object, thus allowing us to test the Game class in isolation of the Decision Engine. This will be demonstrated in the code below.

The following is a code listing of where part 2 ended:

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();
            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());
        }

    }
}

Enums.cs

namespace RockPaperScissors
{
    public enum GameResult
    {
        ComputerWins,
        PlayerWins,
        Draw
    }

    public enum Move
    {
        Rock,
        Paper,
        Scissors
    }
}

Test methods from GameTest.cs

        [TestMethod]
        public void ComputerRock_PlayerPaper_ComputerWins()
        {
            var game = new Game();
            game.PlayerMove = "Paper";
            Assert.AreEqual("Player Wins!", game.Result());
        }

        [TestMethod]
        public void ComputerRock_PlayerScissors_ComputerWins()
        {
            var game = new Game();
            game.PlayerMove = "Scissors";
            Assert.AreEqual("Computer Wins!", game.Result());
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ThrowsErrorIfPlayerMoveIsNotSet()
        {
            var game = new Game();
            game.Result();
        }

Game.cs

namespace RockPaperScissors
{
    public class Game
    {

        private string _playerMove;
        public string PlayerMove
        {
            private get
            {
                if (String.IsNullOrEmpty(_playerMove))
                    throw new ArgumentNullException("PlayerMove");
                return _playerMove;
            }
            set
            {
                _playerMove = value;
            }
        }

        public string Result()
        {
            var engine = new DecisionEngine();
            var gameResult = engine.Result(Move.Rock, (Move) Enum.Parse(typeof (Move), PlayerMove));
            return gameResult == GameResult.PlayerWins ? "Player Wins!" : "Computer Wins!";
        }

    }
}

Test methods from DecisionEngineTest.cs

        [TestMethod]
        public void ComputerRock_PlayerPaper_ComputerWins()
        {
            var engine = new DecisionEngine();
            var gameResult = engine.Result(Move.Rock, Move.Paper);
            Assert.AreEqual(GameResult.PlayerWins, gameResult);
        }

        [TestMethod]
        public void ComputerRock_PlayerScissors_ComputerWins()
        {
            var engine = new DecisionEngine();
            var gameResult = engine.Result(Move.Rock, Move.Scissors);
            Assert.AreEqual(GameResult.ComputerWins, gameResult);
        }
    }

DecisionEngine.cs

namespace RockPaperScissors
{
    public class DecisionEngine
    {
        public GameResult Result(Move computerMove, Move playerMove)
        {
            return playerMove == Move.Paper ? GameResult.PlayerWins : GameResult.ComputerWins;
        }
    }
}

Next we continue with the implementation, moving towards DI. The process of refactoring is as important as the final result. We will improve the design in small, deliberate steps, which minimise the impact of the changes at each step. We will be making changes to the Game class, at present it is as follows:

Test methods from GameTest.cs

        [TestMethod]
        public void ComputerRock_PlayerPaper_ComputerWins()
        {
            var game = new Game();
            game.PlayerMove = "Paper";
            Assert.AreEqual("Player Wins!", game.Result());
        }

        [TestMethod]
        public void ComputerRock_PlayerScissors_ComputerWins()
        {
            var game = new Game();
            game.PlayerMove = "Scissors";
            Assert.AreEqual("Computer Wins!", game.Result());
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ThrowsErrorIfPlayerMoveIsNotSet()
        {
            var game = new Game();
            game.Result();
        }

Game.cs

namespace RockPaperScissors
{
    public class Game
    {

        private string _playerMove;
        public string PlayerMove
        {
            private get
            {
                if (String.IsNullOrEmpty(_playerMove))
                    throw new ArgumentNullException("PlayerMove");
                return _playerMove;
            }
            set
            {
                _playerMove = value;
            }
        }

        public string Result()
        {
            var engine = new DecisionEngine();
            var gameResult = engine.Result(Move.Rock, (Move) Enum.Parse(typeof (Move), PlayerMove));
            return gameResult == GameResult.PlayerWins ? "Player Wins!" : "Computer Wins!";
        }

    }
}

Begin with the tests, we change a single test as follows:

GameTest.cs

        [TestMethod]
        public void ComputerRock_PlayerPaper_ComputerWins()
        {
            var game = new Game(new DecisionEngine());
            game.PlayerMove = "Paper";
            Assert.AreEqual("Player Wins!", game.Result());
        }

We now refactor the Game class (in a safe way) to get the code to compile and to get all tests passing. This is done by turning engine into an instance method, and then introducing two constructors:

Game.cs

namespace RockPaperScissors
{
    public class Game
    {

        private string _playerMove;
        private DecisionEngine _decisionEngine;

        public string PlayerMove
        {
            private get
            {
                if (String.IsNullOrEmpty(_playerMove))
                    throw new ArgumentNullException("PlayerMove");
                return _playerMove;
            }
            set
            {
                _playerMove = value;
            }
        }
        public Game()
        {
            _decisionEngine = new DecisionEngine();
        }

        public Game(DecisionEngine decisionEngine)
        {
            _decisionEngine = decisionEngine;
        }

        public string Result()
        {
            var engine = _decisionEngine;
            var gameResult = engine.Result(Move.Rock, (Move) Enum.Parse(typeof (Move), PlayerMove));
            return gameResult == GameResult.PlayerWins ? "Player Wins!" : "Computer Wins!";
        }

    }
}

All tests should pass, and we can modify the remainder of the Game tests:

GameTest.cs

        [TestMethod]
        public void ComputerRock_PlayerPaper_ComputerWins()
        {
            var game = new Game(new DecisionEngine());
            game.PlayerMove = "Paper";
            Assert.AreEqual("Player Wins!", game.Result());
        }

        [TestMethod]
        public void ComputerRock_PlayerScissors_ComputerWins()
        {
            var game = new Game(new DecisionEngine());
            game.PlayerMove = "Scissors";
            Assert.AreEqual("Computer Wins!", game.Result());
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ThrowsErrorIfPlayerMoveIsNotSet()
        {
            var game = new Game(new DecisionEngine());
            game.Result();
        }

If all tests pass, remove the default constructor and fix the compilation errors.

Game.cs

namespace RockPaperScissors
{
    public class Game
    {

        private string _playerMove;
        private DecisionEngine _decisionEngine;

        public string PlayerMove
        {
            private get
            {
                if (String.IsNullOrEmpty(_playerMove))
                    throw new ArgumentNullException("PlayerMove");
                return _playerMove;
            }
            set
            {
                _playerMove = value;
            }
        }

        public Game(DecisionEngine descisionEngine)
        {
            _decisionEngine = decisionEngine;
        }

        public string Result()
        {
            var engine = _decisionEngine;
            var gameResult = engine.Result(Move.Rock, (Move) Enum.Parse(typeof (Move), PlayerMove));
            return gameResult == GameResult.PlayerWins ? "Player Wins!" : "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 DecisionEngine());
            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());
        }

    }
}

All tests should still be passing.

At this point we are restricted to using a specific implementation of Decision Engine. To removing this coupling we could introduce an interface or an abstract class, both are perfectly valid in terms of DI. In this case, an interface seems a better fit.

Create the new IDecisionEngine interface, alongside the DescisionEngine class, and make the following changes to the code base:

IDecisionEngine.cs

namespace RockPaperScissors
{
    public interface IDecisionEngine
    {
        GameResult Result(Move computerMove, Move playerMove);
    }
}

Implement the new interface in DecisionEngine.cs

namespace RockPaperScissors
{
    public class DecisionEngine: IDecisionEngine
    {
        public GameResult Result(Move computerMove, Move playerMove)
        {
            return playerMove == Move.Paper ? GameResult.PlayerWins : GameResult.ComputerWins;
        }
    }
}

Game.cs now uses the IDecisionEngine abstraction:

namespace RockPaperScissors
{
    public class Game
    {

        private string _playerMove;
        private IDecisionEngine _decisionEngine;

        public string PlayerMove
        {
            private get
            {
                if (String.IsNullOrEmpty(_playerMove))
                    throw new ArgumentNullException("PlayerMove");
                return _playerMove;
            }
            set
            {
                _playerMove = value;
            }
        }

        public Game(IDecisionEngine decisionEngine)
        {
            _descisionEngine = decisionEngine;
        }

        public string Result()
        {
            var engine = _decisionEngine;
            var gameResult = engine.Result(Move.Rock, (Move) Enum.Parse(typeof (Move), PlayerMove));
            return gameResult == GameResult.PlayerWins ? "Player Wins!" : "Computer Wins!";
        }

    }
}

At this point we have implemented the dependency injection pattern, we are injecting an instance into the Game object, through the use of an abstraction (namely an interface). We can now make use of the benefits of this re-design.

At the moment, GameTest is as follows:

GameTest.cs

        [TestMethod]
        public void ComputerRock_PlayerPaper_ComputerWins()
        {
            var game = new Game(new DecisionEngine());
            game.PlayerMove = "Paper";
            Assert.AreEqual("Player Wins!", game.Result());
        }

        [TestMethod]
        public void ComputerRock_PlayerScissors_ComputerWins()
        {
            var game = new Game(new DecisionEngine());
            game.PlayerMove = "Scissors";
            Assert.AreEqual("Computer Wins!", game.Result());
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ThrowsErrorIfPlayerMoveIsNotSet()
        {
            var game = new Game(new DecisionEngine());
            game.Result();
        }

In order for these tests to pass, both Game and Decision Engine need to be working properly. Therefore a failing GameTest would indicate a problem with the Game class or the DecisionEngine class (or possibly a problem with the test itself, but lets exclude that from the discussion).

We would like the Game tests to work regardless of a functional DecisionEngine implementation. If a Game test fails, it should indicate a defect within the Game class and not one of its dependencies. This can be achieved through the use of Mocks.

In Part 1 we added a reference to the mocking library called Moq. While there will be examples of using mocks here, please refer to the Moq documentation for a detailed description on them.

In GameTest, we will replace instances of DecisionEngine with mock instances. We will look at one test in detail.

The specific code under test are the following lines from Game.cs

        public string Result()
        {
            var engine = _descissionEngine;
            var gameResult = engine.Result(Move.Rock, (Move) Enum.Parse(typeof (Move), PlayerMove));
            return gameResult == GameResult.PlayerWins ? "Player Wins!" : "Computer Wins!";
        }

When testing Game.cs we would like to assume that the Decision Engine works correctly. We want to test that the Game object uses the Decision Engine correctly and that the code in Game.cs is correct. Mocks allow us to achieve this.

In GameTest.cs change

        [TestMethod]
        public void ComputerRock_PlayerPaper_ComputerWins()
        {
            var game = new Game(new DecisionEngine());
            game.PlayerMove = "Paper";
            Assert.AreEqual("Player Wins!", game.Result());
        }

To the following:

        [TestMethod]
        public void ReturnsCorrectMessageIfPlayerWins()
        {
            var engineMock = new Mock();
            engineMock.Setup(de => de.Result(Move.Rock, Move.Paper)).Returns(GameResult.PlayerWins);
            var game = new Game(engineMock.Object);
            game.PlayerMove = "Paper";
            var result = game.Result();
            engineMock.VerifyAll();
            Assert.AreEqual("Player Wins!", result);
        }

Using mocks allows us to specify the result being returned when a specified method is called, with given parameters. They also allow us to verify that a specific method was actually called.

The benefit of this is that we get to specify the result that will be returned from the mocked decision engine. The mocked  decision engine will return ‘player wins’ provided that its ‘result’ method is called with the specified values (Rock, Paper).

The following points try to describe the test in more detail, please ignore it if it is causing more confusion:

  • Make a mock for IDecisionEngine
  • Configure the mock
    • When the ‘result’ method is called, with the specified parameters, return ‘player wins’
  • Instantiate a game object, passing in the mocked decision engine
  • set the players move
  • call result on game, which will intern call result on the mocked decision engine
    • the mocked decision engine will return ‘player wins’ when result is called with the specified parameters
  • Get the mock to verify that result was called, with the specified parameters
  • Check that the final result was correct

Alter all the tests as follows:

GameTest.cs

        [TestMethod]
        public void ReturnsCorrectMessageIfPlayerWins()
        {
            var engineMock = new Mock();
            engineMock.Setup(de => de.Result(Move.Rock, Move.Paper)).Returns(GameResult.PlayerWins);
            var game = new Game(engineMock.Object);
            game.PlayerMove = "Paper";
            var result = game.Result();
            engineMock.VerifyAll();
            Assert.AreEqual("Player Wins!", result);
        }

        [TestMethod]
        public void ReturnsCorrectMessageIfComputerWins()
        {
            var engineMock = new Mock();
            engineMock.Setup(de => de.Result(Move.Rock, Move.Scissors)).Returns(GameResult.ComputerWins);
            var game = new Game(engineMock.Object);
            game.PlayerMove = "Scissors";
            var result = game.Result();
            engineMock.VerifyAll();
            Assert.AreEqual("Computer Wins!", result);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ThrowsErrorIfPlayerMoveIsNotSet()
        {
            var engineMock = new Mock();
            var game = new Game(engineMock.Object);
            game.Result();
        }

Now the tests are testing the Game class in isolation of the Decision Engine. We have narrowed the unit that we are testing. The SpecFlow tests are still testing the integration of the separate units e.g. Game and Decision Engine.

Until now I have ignored the fact that the game has a random element to it i.e. the computer choice should be random. Randomness can cause problems when testing. We handle this in upcoming posts.

10 thoughts on “Tutorial – BDD and Dependency Injection in .Net (3)

  1. Pingback: Tutorial – BDD and Dependency Injection in .Net (4) | The Ramblings of Daryn Holmes

  2. Hi Daryn, I couldn’t get the Mock to instantiate:-
    code:-
    Mock engineMock = new Mock();
    leads to:-
    Cannot create an instance of the abstract class or interface ‘Moq.Mock’ (CS0144) – C:DevVS2012SpecflowVS2012SpecflowRockPaperScissorsRockPaperScissorsTestTestsGameTest.cs:39,22
    I’m using NUnit rather than MSTest, and .net3.5 as 4 doesn’t seem to work at all in SharpDevelop with SpecFlow 1.9.
    Any ideas?

  3. Dave G.,
    To get the example to work, I had to change:
    var engineMock = new Mock();
    to:
    Mock engineMock = new Mock();

  4. I doubt that will solve the problem.

    @dave G, your Mock declaration needs to know what type it is supposed to mock so try:

    var engineMock = new Mock();

  5. wow, that stripped my “greater than” and “less than” symbols so I will spell it out with HTML entity codes:

    var engineMock = new Mock>DecisionEngine<();

Leave a Reply

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