FizzBuzz

A little while ago, I had a discussion with one of my colleges with regards to documentation. His argument was that the best documentation was the code. My argument was contrary to that.

I thought I would discuss my thoughts here.

Te get things started, lets say you have the below code…

package com.beanietech.fizzbuzz;

public class FizzBuzz {

    /*
     Extra fizz for your buzz!
     */
    public static int fizzbuzz(int fizz, int buzz) {
        while (buzz != 0) {
            int awesome = (fizz & buzz);

            fizz = fizz ^ buzz;

            buzz = awesome << 1;
        }
        return fizz;
    }

}

I’ve changed the variable names to make it a little obscure, but given its only half a dozen lines of code, can you tell me in 10 seconds what it does?

My guess is that you can’t. I couldn’t.

I could write a word document that describes the functionality, it could say…

Given 2 numbers, the two numbers are multiplied together and the result is returned.

Ah, now you say, I know what it does!

Are you sure? Word documents can be incorrect or out of date. You cannot rely on them.

So what is next?

This is where my argument comes in. My thoughts are that the best documentation is the tests. Not the code, and not the description of what the document does.

So lets write a test.

import com.beanietech.fizzbuzz.FizzBuzz;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 *
 * @author hpaff
 */
public class TestFizzBuzz {
    
    public FizzBuzz fizzBuzz;
    
    
    public TestFizzBuzz() {
    }
    
    @BeforeClass
    public static void setUpClass() {
    }
    
    @AfterClass
    public static void tearDownClass() {
    }
    
    @Before
    public void setUp() {
        //fizzBuzz = new FizzBuzz();
    }
    
    @After
    public void tearDown() {
    }

    
    
     @Test
     public void testFizzBuzz1() {
         assertEquals(4,FizzBuzz.fizzbuzz(1, 1) );
     }
     
     @Test
     public void testFizzBuzz2() {
        assertEquals(4,FizzBuzz.fizzbuzz(2, 2));
     }
     
     @Test
     public void testFizzBuzz3() {
           assertEquals(6,FizzBuzz.fizzbuzz(3, 3));
     }
     @Test
     public void testFizzBuzz4() {
           assertEquals(8,FizzBuzz.fizzbuzz(4, 4));
     }

}

As you can see, the tests clearly show what the functionality is.

When we run it, We get confirmation.

Test

So now we know that the method adds two numbers.

Now I hear you say, “Tests are not very user friendly, how is a business person suppose to understand this?”

Well, this is where my second part of this discussion comes in.

There is an open source project called Concordion that can help with this.

Concordion allows you to specify the functionality as examples into an HTML document. It uses the “<span>” tag to mark out sections of the document to be executed.

So, for our little example, our HTML specification would be:

<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html xmlns:concordion="http://www.concordion.org/2007/concordion">
    <head>
        <title>Test FizzBuzz Concordion</title>
    </head>
    <body>
        <h1>Test FizzBuzz with Concordion</h1>
        
        <p>If the Fizz is <span concordion:set="#fizz">1</span> and the Buzz is <span concordion:set="#buzz">1</span> then the FizzBuzz should be <span concordion:assertEquals="fizzBuzz(#fizz, #buzz)">2</span></p>
        
        <p>If the Fizz is <span concordion:set="#fizz">2</span> and the Buzz is <span concordion:set="#buzz">2</span> then the FizzBuzz should be <span concordion:assertEquals="fizzBuzz(#fizz, #buzz)">4</span></p>
        
        <p>If the Fizz is <span concordion:set="#fizz">3</span> and the Buzz is <span concordion:set="#buzz">3</span> then the FizzBuzz should be <span concordion:assertEquals="fizzBuzz(#fizz, #buzz)">6</span></p>

        <p>If the Fizz is <span concordion:set="#fizz">4</span> and the Buzz is <span concordion:set="#buzz">4</span> then the FizzBuzz should be <span concordion:assertEquals="fizzBuzz(#fizz, #buzz)">8</span></p>

    </body>
</html>

Which renders to:

Test FizzBuzz with Concordion

If the Fizz is 1 and the Buzz is 1 then the FizzBuzz should be 2

If the Fizz is 2 and the Buzz is 2 then the FizzBuzz should be 4

If the Fizz is 3 and the Buzz is 3 then the FizzBuzz should be 6

If the Fizz is 4 and the Buzz is 4 then the FizzBuzz should be 8

 

All very readable.

The next step is to add a fixture.  A fixture is a bit of code that links the HTML page to the system you are trying to test.

For this simple execution, the fixture is:

import static com.beanietech.fizzbuzz.FizzBuzz.fizzbuzz;
import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@RunWith(ConcordionRunner.class)
public class FizzBuzzFixture {
   
  public int fizzBuzz(int fizz, int buzz){
      return fizzbuzz(fizz, buzz);
  }

}

Now, when we execute the fixture, we get:

fizzbuzzconcordion

As you can seem the results are green. If for example, we got something wrong, we would get.

fizzbuzzconcordionerror

So, if we ties this into a continuous integration suite such as Jenkins or Bamboo, we should always have up to date documentation that has been verified and human readable.

Now, if we need to add more tests, we no longer need to write code for this particular scenario. Why? Because we have decoupled “What we are testing” from “How we are testing“.

The fixture controls how we test the method, but the HTML page controls what we are testing. So, to add more tests, provided we haven’t changed any functionality, we just need to modify the HTML page.

Finally, in this example I used Concordion. But there are other tools out there such as Fitnesse which is wiki based, Cucumber and JBehave which is Text file based and numerous others.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.