Bowling Game Kata Part Three - The Normal Score Game

Let's write our next few tests. In these tests we score a normal game in which there are no strikes or spares. We throw the ball down the lane and knock down a pin or two.

Normal Game

BowlingGame.Test/GameTest.cs
[Test]
public void Game_AllOnes_ReturnsScore()
{
    var game = new Game();

    for (int i = 0; i < 20; i++)
    {
        game.Roll(1);
    }

    Assert.AreEqual(20, game.Score());
}

Let's compile and run the test.

1 test failed "Expected 20 but was 0"

We need to get this code passing. Looking at the score method we are returning a 0. This is because we are at the "constant" stage of Uncle Bob's Transformation Priority Premise. We need to move to the level of "scalar". This will be a variable to hold the score of the game.

BowlingGame/Game.cs
private int score;

public void Roll(int pins)
{
    score += pins;
}

public int Score()
{
    return score;
}

Compile and run the test.

2 tests passed

Now that our tests are passing, we can do some refactoring.

  1. Both tests instantiate a new Game.
  2. Both tests have a loop that rolls the ball.

Let's extract this duplicate code out.

BowlingGame.Test/GameTest.cs
private Game game;

[SetUp]
public void TestSetup()
{
    game = new Game();
}

[Test]
public void Game_AllZeros_ReturnsScore()
{
    RollMany(20, 0);

    Assert.AreEqual(0, game.Score());
}

[Test]
public void Game_AllOnes_ReturnsScore()
{
    RollMany(20, 1);

    Assert.AreEqual(20,
    game.Score());
}

private void RollMany(int rolls, int pins)
{
    for (int i = 0; i < rolls; i++)
    {
        game.Roll(pins);
    }
}

The SetUp attribute indicates code that should run before each test. Before each test runs, we want to instantiate a new Game.

We created a RollMany method to take care of the loop.

At the end of the Normal Game tests, our code looks like this:

BowlingGame/Game.cs
namespace BowlingGame
{
    public class Game 
    {
        private int score;

        public void Roll(int pins) 
        {
            score += pins;
        }

        public int Score()
        {
            return score;
        }
    }
}
BowlingGame.Test/GameTest.cs
using NUnit.Framework;
            
namespace BowlingGame.Test
{
    [TestFixture]
    public class GameTest
    {
        private Game game;

        [SetUp]
        public void TestSetup()
        {
            game = new Game();
        }

        [Test]
        public void Game_AllZeros_ReturnsScore()
        {
            RollMany(20, 0);

            Assert.AreEqual(0,
            game.Score());
        }

        [Test]
        public void Game_AllOnes_ReturnsScore()
        {
            RollMany(20, 1);

            Assert.AreEqual(20,
            game.Score());
        }

        private void RollMany(int rolls, int pins)
        {
            for (int i = 0; i < rolls; i++)
            {
                game.Roll(pins);
            }
        }
    }
}