Wednesday, May 7, 2014

Test Drive that Fizz Buzz

Fizz Buzz is a game where you count upwards saying the numbers aloud with a slight twist to try and trip you up. When a number is divisible by 3 you say "Fizz", when a number is divisible by 5 you say "Buzz" and if the number is divisible by both 3 and 5 you say "FizzBuzz". The straight forward rules of Fizz Buzz makes it an ideal problem to solve as a TDD kata as it allows you to focus on TDD practices as opposed to the implementation details.

The goal of a TDD kata is to allow you to follow the red, green, refactor pattern over and over. The idea is to first write a failing test (red), write the minimum implementation to make that test pass (green) and then refactor your code as necessary to make it more maintainable / readable.

On with the kata...

Test 1 - Instantiating FizzBuzzer does not throw an exception
Write the failing test:

1
2
3
4
5
[Test]
public void FizzBuzzer_Instantiating_ShouldNotThrowAnException()
{
  Assert.DoesNotThrow(() => new FizzBuzzer());
}
Red - This test will not compile as the FizzBuzzer class does not exist yet.

Write the minimum so the test will pass:

1
2
3
public class FizzBuzzer
{
}
Green - The test now passes.

Refactor - Nothing to refactor.

Test 2 - Value not divisible by three or five returns value
Write the failing test:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Test]
public void Convert_ValueNotDivisibleByThreeOrFive_ShouldReturnValue()
{
  //Arrange
  int valueToConvert = 1;
  string expectedValue = "1";
  FizzBuzzer fb = new FizzBuzzer();

  //Act
  string result = fb.Convert(valueToConvert);

  //Assert
  Assert.That(result, Is.EqualTo(expectedValue));
}
Red - This test will not compile as the FizzBuzzer class currently does not have a Convert method. The test follows the "Triple A" unit test structure. This is generally considered a best practice when writing unit tests to provide them with a consistent structure. The test also utilizes NUnit's Assert-That-Is syntax for its assertions which makes the assertions themselves more human readable - For example the above assertion reads like "Assert that result is equal to expectedValue".

Write the minimum so the test will pass:

1
2
3
4
public string Convert(int valueToConvert)
{
  return valueToConvert.ToString();
}
Green - The test now passes and the minimum functionality has been written. It is tempting to write more than is actually required but resist this otherwise you will find yourself writing additional unit tests after implementing the functionality or gold plating your code with functionality you necessarily do not actually need.

Refactor - Nothing to refactor.

Test 3 - Value divisible by three returns "Fizz"
Write the failing test:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Test]
public void Convert_ValueDivisibleByThree_ShouldReturnFizz()
{
  //Arrange
  int valueToConvert = 3;
  string expectedValue = "Fizz";

  FizzBuzzer fb = new FizzBuzzer();

  //Act
  string result = fb.Convert(valueToConvert);

  //Assert
  Assert.That(result, Is.EqualTo(expectedValue));
}
Red - The test fails as it will return "3".

Write the minimum so the test will pass:

1
2
3
4
5
6
7
public string Convert(int valueToConvert)
{
  if (valueToConvert % 3 == 0)
    return "Fizz";

  return valueToConvert.ToString();
}
Green - The test passes as "Fizz" is returned when valueToConvert is divisible by three - The modulus arithmetic operator is used to check for this.

Refactor - There is some refactoring to be done here. There is some duplication now within the previous two tests: FizzBuzzer fb = new FizzBuzzer(); This can be extracted to a test setup method that is run before each test is executed.

Refactored test code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
private FizzBuzzer _fb;

[SetUp]
public void Setup()
{
  _fb = new FizzBuzzer();
}

[Test]
public void Convert_ValueNotDivisibleByThreeOrFive_ShouldReturnValue()
{
  //Arrange
  int valueToConvert = 1;
  string expectedValue = "1";   

  //Act
  string result = _fb.Convert(valueToConvert);

  //Assert
  Assert.That(result, Is.EqualTo(expectedValue));
}
A private FizzBuzzer (_fb) variable has been declared with class scope. This is instantiated within the Setup method which is called before each test is executed because of it's [Setup] attribute. After refactoring your code, always re-run your unit tests. This practice will help ensure your refactoring has not broken anything.

The //Act and //Assert steps within each test are also duplicated and could be extracted out into a separate method. However, to help practice the "Triple A" pattern I have chosen to leave them in each of the unit tests.

Test 4 - When value is divisible by five return "Buzz"
Write the failing test:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Test]
public void Convert_ValueDivisibleByFive_ShouldReturnBuzz()
{
  //Arrange
  int valueToConvert = 5;
  string expectedValue = "Buzz";

  //Act
  string result = _fb.Convert(valueToConvert);

  //Assert
  Assert.That(result, Is.EqualTo(expectedValue));
}
Red - The test fails as it returns "5".

Write the minimum so the test will pass:

1
2
3
4
5
6
7
8
9
public string Convert(int valueToConvert)
{
  if (valueToConvert % 3 == 0)
    return "Fizz";
  if (valueToConvert % 5 == 0)
    return "Buzz";

  return valueToConvert.ToString();
}
Green - The test passes

Refactor - Nothing to refactor

Test 5 - When a value is divisible by three and fizz return "FizzBuzz"
Write the failing test:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Test]
public void Convert_ValueDivisibleByThreeAndFive_ShouldReturnFizzBuzz()
{
  //Arrange
  int valueToConvert = 15;
  string expectedValue = "FizzBuzz";

  //Act
  string result = _fb.Convert(valueToConvert);

  //Assert
  Assert.That(result, Is.EqualTo(expectedValue));
}
Red - The test fails as it returns "Fizz".

Write the minimum so the test will pass:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public string Convert(int valueToConvert)
{
  if (valueToConvert % 3 == 0 && valueToConvert % 5 == 0)
    return "FizzBuzz";
  if (valueToConvert % 3 == 0)
    return "Fizz";
  if (valueToConvert % 5 == 0)
    return "Buzz";

  return valueToConvert.ToString();
}
Green - The test passes.

Refactor - The Convert method has some duplicate modulus calculations which can be refactored and some improvements can be made to make the method more readable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public string Convert(int valueToConvert)
{
  bool isFizz = valueToConvert % 3 == 0;
  bool isBuzz = valueToConvert % 5 == 0;

  if (isFizz && isBuzz)
    return "FizzBuzz";
  if (isFizz)
    return "Fizz";
  if (isBuzz)
    return "Buzz";

  return valueToConvert.ToString();
}
This refactoring extracts the fizz and buzz modulus calculations into their own boolean variables to represent if valueToConvert is divisible by three or five. These boolean variables are used to return the correct conversion. This refactoring has rippled throughout the whole of the convert method - Running the previously written unit tests will prove that no functionality has been broken.

That completes the Fizz Buzz TDD kata.

Continuing further, you could re-factor the FizzBuzzer implementation to only use a single return statement and use the existing unit tests to confirm you have not broken any functionality. This is an important factor to remember when writing a unit test: The test should not know about any of the implementation details of what it is testing. See my blog post about a ##DDD North## session I attended to read more about this.

The projects full source code can be found here.

Fizz Buzz is an excellent problem to help you get into the TDD mindset. It allows you to focus on the red, green, refactor pattern without becoming too bogged down with implementation details. To help keep things interesting, you can also attempt many different implementations of the FizzBuzzer class when you attempt the kata.

No comments:

Post a Comment