Archive for » March 10th, 2013 «

Let’s play tennis, with TDD

TDD & Code Kata’s

Now I’m working since more than four months as a software engineer at bbv Software Services in Switzerland. Every new employee at bbv needs to attend several courses (bbv Academy) to improve and extend his skills towards a good software developer. After my Scrum Master course, the next one on my schedule was Test Driven Development.

In addition to the theory why TDD, clean code and pair programming is highly desirable the course was accompanied with hands-on code kata’s. For those who don’t know what a code kata is, here is a citation from Wikipedia:

A code kata is an exercise in programming which helps a programmer hone their skills through practice and repetition.

One kata of the course was the Tennis Scorer. The goal was to implement a mechanism, which prints the score of a single game (therefore there are no sets).

Our Referee

The rules given were the following:

  1. A game is won by the first player to have won at least four points in total and at least two points more than the opponent.
  2. The running score of each game is described in a manner peculiar to tennis: scores from zero to three points are described as “love”, “fifteen”, “thirty”, and “forty” respectively.
  3. If at least three points have been scored by each player, and the scores are equal, the score is “deuce”.
  4. If at least three points have been scored by each side and a player has one more point than his opponent, the score of the game is “advantage” for the player in the lead.

Our implementation should be based on the ITennisScorer interface, which of yourse was changed during the exercise.

</span>
<pre>namespace Tennis
{
    public interface ITennisScorer
    {
        void AchievesScore(Player player);

        string GameScore { get; }
    }

    public enum Player
    {
        PlayerA,
        PlayerB
    }
}

Because it was a TDD course, we needed to build up a TODO list of appropriate unit tests first (e.g. Game just started, no one scored so far, expected score “love”).

Then we implented the unit tests one by one. Each of them had to fail first. Then came the actual implementation, test should pass afterwards. Code Refactoring -> All tests should pass.

Our Players

My initial implementation was blotched with many if statements, handling all the special cases for the game score (deuce, advantage, …). I also had code duplication, because I needed to track and update the score of player A and B separately.

But here comes the ultimate advantage of TDD. You’re not afraid anymore to refactor your code to a better architecture and solution. Because you know you can (and should) always run your unit tests and see if everything is still behaving as desired. You can sleep comfortably without to fear the next morning from an upcoming design change. Ok, I know. Some can still sleep during the nights without TDD, but they will be haunted by bad dreams 😉

So I refactored my code and eliminated the code duplication. Instead of using an enum to track the actual player score, I simply use a two-dimensional array, which holds all the possible scores of a game.

namespace Tennis
{
    public class TennisScorer : ITennisScorer
    {
        private int scoreA;
        private int scoreB;

        private const string GameA = "gameA";
        private const string GameB = "gameB";
        private const string AdvantageA = "advantageA";
        private const string AdvantageB = "advantageB";

        private static readonly string[][] PlayerScores = new[]
            {
                new[] {"love",  "15:0",     "30:0",     "40:0",     GameA},
                new[] {"0:15",  "15:15",    "30:15",    "40:15",    GameA},
                new[] {"0:30",  "15:30",    "30:30",    "40:30",    GameA},
                new[] {"0:40",  "15:40",    "30:40",    "deuce",    AdvantageA},
                new[] { GameB,   GameB,     GameB,      AdvantageB}
            };

        public void AchievesScore(Player player)
        {
            switch (player)
            {
                case Player.PlayerA:
                    UpdateGameScore(ref scoreA, ref scoreB);
                    break;
                case Player.PlayerB:
                    UpdateGameScore(ref scoreB, ref scoreA);
                    break;
            }
        }

        public string GameScore
        {
            get { return PlayerScores[scoreB][scoreA]; }
        }

        private void UpdateGameScore(ref int playerToAddScore, ref int opponentScore)
        {
            if (GameScore == AdvantageA || GameScore == AdvantageB)
            {
                opponentScore--;
            }
            else if (GameScore != GameA && GameScore != GameB)
            {
                playerToAddScore++;
            }
        }
    }
}

The Regulating Authority

I also refactored my unit tests. There were many tests, testing all the possible game processes. But my course instructor gave me the hint to use Nunit’s RowTest. Instead of writing many, almost identical tests for each case, I wrote just one parametrized unit test and the many case were provided as a parameter.

namespace Tennis.Test
{
    using NUnit.Framework;
    using FluentAssertions;

    [TestFixture]
    public class TennisScorerTest
    {
        private TennisScorer testee;

        [SetUp]
        public void SetUp()
        {
            testee = new TennisScorer();
        }

        [TestCase("", "love")]
        [TestCase("A", "15:0")]
        [TestCase("AAB", "30:15")]
        [TestCase("AAAB", "40:15")]
        [TestCase("BBB", "0:40")]
        [TestCase("BBB", "0:40")]
        [TestCase("ABABAB", "deuce")]
        [TestCase("AAABBB", "deuce")]
        [TestCase("AABBAB", "deuce")]
        [TestCase("AAABBBA", "advantageA")]
        [TestCase("AAABBBB", "advantageB")]
        [TestCase("AAAA", "gameA")]
        [TestCase("AAAAA", "gameA")]
        [TestCase("AAAABBBBBB", "gameA")]
        [TestCase("AAABBBBB", "gameB")]
        [TestCase("ABABABABABABABABABABABAA", "gameA")]
        public void PlayerScores(string game, string expectedScores)
        {
            foreach (var currentScore in game)
            {
                switch (currentScore)
                {
                    case 'A':
                        testee.AchievesScore(Player.PlayerA);
                        break;
                    case 'B':
                        testee.AchievesScore(Player.PlayerB);
                        break;
                }
            }
            testee.GameScore.Should().Be(expectedScores);
        }
    }
}

References & Coaches

If you want to practice your programming skills, I can encourage you to do some code kata’s, or similar challenges, to stay fresh in your head.

Here is a list of some useful sites with some brain-teasers and, if given, their solutions:

My Tennis Scorer implementation is available on GitHub.

Category: C#, Coding  Tags: , ,  One Comment