FizzBuzz Kata

1… 2… Fizz! 4… Buzz!

Rules of Testing

  • First Rule: You can write no production code except to pass a failing test case
  • Second Rule: You can only write enough of a test case to demonstrate a failure
  • Third Rule: You can only write enough production code to pass the currently failing test case

FizzBuzz Game

  • "Fizz" if a number is divisible by 3
  • "Buzz" if a number is divisible by 5
  • "FizzBuzz" if a number is divisible by 3 and 5
  • Otherwise, show the number (let's show the number as a String)

Project Setup

  • Open Git Bash
  • cd into your eclipse workspace
  • mkdir fizzbuzz
  • cd into the fizzbuzz directory
  • copy and paste the following: curl https://raw.githubusercontent.com/WeCanCodeIT/gradle-scripts/master/basic-junit/build.gradle --output build.gradle
  • gradle eclipse
  • Open Eclipse and import existing project into workspace
  • Create a package named fizzbuzz in src/test/java
  • Create a class named FizzBuzzTest in your new package

Red

package fizzbuzz;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class FizzBuzzTest {
    @Test
    public void shouldReturnOneAsString() {
        // Arrange
        FizzBuzz underTest = new Fizzbuzz();
        // Act
        String actual = underTest.parse(1);
        // Assert
        assertEquals("1", actual);
    }
}

First Failures

So all of our failures at this point are compilation failures. To fix these, let's let our IDE fix some things for us…

All fixed!

So now we have the following generated class in src/main/java and in our fizzbuzz package.

package fizzbuzz;

public class FizzBuzz {
    public String parse(int i) {
        // TODO Auto-generated method stub
        return null;
    }
}

Now run your tests to see this one fail (<ctrl> + <R>).

Red -> Green

Now, we do the SIMPLEST thing to make the test pas. In this case…

package fizzbuzz;

public class FizzBuzz {
    public String parse(int i) {
        return "1";
    }
}

Remember, never write more code than you need to make the test pass. Even if you know how the code may change in the future. Be creative with your tests and let them dictate how your code should work.

Now, run your test to see it passing!

Green -> Refactor

Now that we have gotten to green in the red -> green -> refactor cycle, it's time to refactor! Remember, refactoring can take the form of properly naming things, deleting unnecessary code, removing duplication, or anything else that generally makes our code cleaner.

package fizzbuzz;

public class FizzBuzz {
    public String parse(int number) {
        return "1";
    }
}

This may seem like a small detail now, but proper naming conventions are a tremendous part of clean code. Never neglect the impact a good namespacing convention can have on a codebase.

Refactor -> Red

Now that we've got a passing test for 1, let's write a test for 2.

public class FizzBuzzTest {
    ...
    @Test
    public void shouldReturnTwoAsString() {
        // Arrange
        FizzBuzz underTest = new FizzBuzz();

        // Act
        String actual = underTest.parse(2);

        // Assert
        assertEquals("2", actual);
    }
}

Run this test to see it fail.

Red -> Green

So let's change our class under test to make this test pass…

package fizzbuzz;

public class FizzBuzz {
    public String parse(int number) {
        if (number == 1) {
            return "1";
        }
        return "2";
    }
}

Remember, since return is a break statement, we will never run the risk of returning two outcomes from the same method. Run your tests to see them pass.

Green -> Refactor

Time to make our code cleaner again. Now let's take a look at the logic of our method and think about how we can make it cleaner. At this point it's pretty clear that we are just returning a String version of whatever int we pass in. Let's make our method do that instead…

package fizzbuzz;

public class FizzBuzz {
    public String parse(int number) {
        return "" + number;
    }
}

We're using string concatenation to force the result to be a String!

Refactor -> Red

Time for our third test!

@Test
public void shouldReturnFizzForThree() {
    // Arrange
    FizzBuzz underTest = new FizzBuzz();

    // Act
    String actual = underTest.parse(3);

    // Assert
    assertEquals("Fizz", actual);
}

Run this to see the fail.

Red -> Green

Remember to do the simplest thing to make the test pass.

package fizzbuzz;

public class FizzBuzz {
    public String parse(int number) {
        if (number == 3) {
            return "Fizz";
        }
        return "" + number;
    }
}

Green -> Red

Since there really isn't anything to refactor, it's time to turn red again! So, for our next test, we are going to choose 5.

@Test
public void shouldReturnBuzzForFive() {
    // Arrange
    FizzBuzz underTest = new FizzBuzz();

    // Act
    String actual = underTest.parse(5);

    // Assert
    assertEquals("Buzz", actual);
}

Why 5 and not 4?

Red -> Green

package fizzbuzz;

public class FizzBuzz {
    public String parse(int number) {
        if (number == 5) {
            return "Buzz";
        }
        if (number == 3) {
            return "Fizz";
        }
        return "" + number;
    }
}

We now have a passing test for 5!

Green -> Red

Time to continue on our testing journey…

@Test
public void shouldReturnFizzForSix() {
    // Arrange
    FizzBuzz underTest = new FizzBuzz();

    // Act
    String actual = underTest.parse(6);

    // Assert
    assertEquals("Fizz", actual);
}

This gets us failing. Let continue on the simplest road to passing.

Red -> Green

package fizzbuzz;

public class FizzBuzz {
    public String parse(int number) {
        if (number == 5) {
            return "Buzz";
        }
        if (number == 3 || number == 6) {
            return "Fizz";
        }
        return "" + number;
    }
}

Green -> Refactor

Now let's examine our parse method and see how we can make this logic a little more clear

package fizzbuzz;

public class FizzBuzz {
    public String parse(int number) {
        if (number == 5) {
            return "Buzz";
        }
        if (number % 3 == 0) {
            return "Fizz";
        }
        return "" + number;
    }
}

So now we're using the modulus operator to see if the number we have is evenly divisible by 3. We do this by checking to see if zero is the remainder when we divide by 3.

Complete remaining test cases

Time to continue using this same logic to figure out the remaining numbers to test! Which numbers make sense to test? Which do we have enough confidence to skip at this point?