Monday, February 14, 2011

Test-Driven Development of FizzBuzz

It's been a while since I last posted an entry here. So, let me take some time out to go back to basics. I've recently given a short talk about TDD (Test-Driven Development) using JUnit 4 to some young Java developers. I chose the FizzBuzz problem as an example.

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz.” For numbers which are multiples of both three and five print “FizzBuzz.”


I started with a test. I also decided to have the class return a string (instead of printing it out onto the console).


import static org.junit.Assert.*;

import org.junit.Test;

public class FizzBuzzTest {
private final FizzBuzz fizzbuzz = new FizzBuzz();
}


With this, I had to create the corresponding class.


public class FizzBuzz {
}


I continue with adding tests.


import static org.junit.Assert.*;

import org.junit.Test;

public class FizzBuzzTest {
private final FizzBuzz fizzbuzz = new FizzBuzz();

@Test
public void returnsNumber() throws Exception {
assertEquals("1", fizzbuzz.of(1));
}
}


With this, I had to create the corresponding method and make it pass.


public class FizzBuzz {
public String of(int number) {
return String.valueOf(number);
}
}


I continue to add more tests.


import static org.junit.Assert.*;

import org.junit.Test;

public class FizzBuzzTest {
private final FizzBuzz fizzbuzz = new FizzBuzz();

@Test
public void returnsNumber() throws Exception {
assertEquals("1", fizzbuzz.of(1));
assertEquals("2", fizzbuzz.of(2));
}

@Test
public void returnsFizzWhenNumberIsMultipleOfThree() throws Exception {
assertEquals("Fizz", fizzbuzz.of(3));
}
}


With this, I had to add code and make it pass.


public class FizzBuzz {
public String of(int number) {
if (number % 3 == 0) {
return "Fizz";
}
return String.valueOf(number);
}
}


I continue to add more tests.


import static org.junit.Assert.*;

import org.junit.Test;

public class FizzBuzzTest {
private final FizzBuzz fizzbuzz = new FizzBuzz();

@Test
public void returnsNumber() throws Exception {
assertEquals("1", fizzbuzz.of(1));
assertEquals("2", fizzbuzz.of(2));
}

@Test
public void returnsFizzWhenNumberIsMultipleOfThree() throws Exception {
assertEquals("Fizz", fizzbuzz.of(3));
assertEquals("Fizz", fizzbuzz.of(6));
assertEquals("Fizz", fizzbuzz.of(9));
}

@Test
public void returnsBuzzWhenNumberIsMultipleOfFive() throws Exception {
assertEquals("Buzz", fizzbuzz.of(5));
}
}


With this, I had to add code and make it pass.


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


I continue to add more tests.


import static org.junit.Assert.*;

import org.junit.Test;

public class FizzBuzzTest {
private final FizzBuzz fizzbuzz = new FizzBuzz();

@Test public void
returnsNumber() throws Exception {
assertEquals("1", fizzbuzz.of(1));
assertEquals("2", fizzbuzz.of(2));
}

@Test public void
returnsFizzWhenNumberIsMultipleOfThree() throws Exception {
assertEquals("Fizz", fizzbuzz.of(3));
assertEquals("Fizz", fizzbuzz.of(6));
assertEquals("Fizz", fizzbuzz.of(9));
}

@Test public void
returnsBuzzWhenNumberIsMultipleOfFive() throws Exception {
assertEquals("Buzz", fizzbuzz.of(5));
assertEquals("Buzz", fizzbuzz.of(10));
}

@Test public void
returnsFizzBuzzWhenNumberIsMultipleOfThreeAndFive() throws Exception {
assertEquals("FizzBuzz", fizzbuzz.of(15));
}
}


With this, I had to add code and make it pass.


public class FizzBuzz {
public String of(int number) {
if (number % 3 == 0) {
return "Fizz";
}
if (number % 5 == 0) {
return "Buzz";
}
if (number % 3 == 0 && number % 5 == 0) {
return "FizzBuzz";
}
return String.valueOf(number);
}
}


At this point, my previous tests fail. So, guided by tests, I went back and fixed things up.


public class FizzBuzz {
public String of(int number) {
if (number % 3 == 0 && number % 5 == 0) {
return "FizzBuzz";
}
if (number % 3 == 0) {
return "Fizz";
}
if (number % 5 == 0) {
return "Buzz";
}
return String.valueOf(number);
}
}


Now that all of the tests are passing, I thought about refactoring (by removing duplication). Obviously, the "Fizz" and "Buzz" strings are repeated. Again, guided by tests, I changed my code to something like:


public class FizzBuzz {
public String of(int number) {
StringBuilder b = new StringBuilder();
if (number % 3 == 0) {
b.append("Fizz");
}
if (number % 5 == 0) {
b.append("Buzz");
}
if (number % 3 != 0 && number % 5 != 0) {
return b.append(String.valueOf(number));
}
return b.toString();
}
}


As I glance over the code, I can see there's still some duplication. The modulus was operating on the number twice: one by three, and another by five. Again, guided by tests, I modified my code to remove duplication. Here's the result.


public class FizzBuzz {
public String of(int number) {
boolean multipleOf3 = (number % 3 == 0);
boolean multipleOf5 = (number % 5 == 0);
if (!multipleOf3 && !multipleOf5) {
return String.valueOf(number);
}
StringBuilder b = new StringBuilder();
if (multipleOf3) {
b.append("Fizz");
}
if (multipleOf5) {
b.append("Buzz");
}
return b.toString();
}
}


I know this is simple. But it's a good coding exercise.