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.
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:
As you can seem the results are green. If for example, we got something wrong, we would get.
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.