According to the popular story (backed up by family letters), the fourteen-year-old Mozart was visiting Rome, when he first heard the piece during the Wednesday service. Later that day, he wrote it down entirely from memory, returning to the Chapel that Friday to make minor corrections. Miserere (Allegri) at Wikipedia

A couple of days ago, J. B. Rainsberger wrote on Twitter:

Potential experiment at future conference: ask experienced TDD practitioners to test-drive a problem in their head. "TDD like Mozart" J. B. Rainsberger

I immediately thought: “Hey, that sounds interesting!”. It sounds like a hard but fun challenge. So I came up with this little exercise (probably not exactly what J. B. Rainsberger was thinking of):

Rules

  • Find a small problem you want to solve in code
  • You can use all the documentation you need, but...
  • Do not write down anything while you are "composing" the code

“Compose” the solution:

  • Come up with a test entirely in your head. Think about it in enough detail so you could write all the code down and it would compile and fail. Think about why it would fail.
  • "Compose" the code that makes the test green in your head. Again, think of all the little details, like imports, syntax, exceptions, things that might go wrong, ...
  • Refactor the code in your head

Write it down from memory:

  • Close all the documentation you used during composing
  • Now, write down your solution from memory.
  • Use a simple text editor, not an IDE. Syntax highlighting is (probalby) OK, though.
  • Do not change a line that you have already written
  • When you have written a file, close it and do not look at it again
  • Do not use copy+paste
  • Compile and run

Come back on friday to make minor corrections:

  • Change the code so it runs and compiles
  • Write down all the changes you had to make

The Exercise

This is probably a very difficult exercise. But it sounds like fun, so I’ll try it. I will implement a server for the “ECHO” protocol (defined in RFC862). I’ll be right back and post the solution. You have to trust me that I really followed the rules. I don’t expect the code to compile anyway ;) So, please wait here while I’ll do the exercise…

Thanks for waiting! I did not record a screen cast, because you’d see me reading documentation and doing nothing for most of the time. I wrote down the source code from memory, without changing lines I already wrote. Here I will add some comments about my thinking process between the files. The solution is a bit over-engineered. This is because I really wanted to include refactoring steps in my thinking process.

I started with the following gradle build script (which I wrote before doing the exercise):

build.gradle<pre> apply plugin: ‘java’

repositories { mavenCentral() maven { url ‘http://files.couchbase.com/maven2/’ } } dependencies { testCompile ‘junit:junit:4.+’ testCompile ‘org.mockito:mockito-core:1.9.5’ } </pre>

First, let’s start with an acceptance test. I thought about this test first. It describes the desired functionality from a user’s perspecive. This test exercises the whole system. This test is there to make sure the system contains the desired functionality, not to drive the design or make sure it is implemented correctly. I will think of unit tests for those purposes later.

EchoServiceTest.java<pre> import org.junit.*;

public class EchoServiceTest { @Test public void sendsBackLineOfText() { EchoService echoService = new EchoService(); echoService.start();

    Socket socket = new Socket("localhost", 7);
    try {
        socket.getOutputStream().write("Hello World\n");

        byte[] buffer = new byte[128];
        int numResponseBytes = socket.getInputStream().read(buffer);
        String response = new String(buffer, 0, numResponseBytes);

        assertEquals("Hello World\n", response);
    } finally {
        socket.close();
    }
} }</pre>

This test is too broad to make it green in one go. So I need another test. I will start with a server that makes sure all requests are served.

EchoServerTest.java<pre> import org.junit.; import static org.mockito.Mockito.; import java.net.*;

public class EchoServerTest { @Test public void delegatesServingTheClientToEchoResponder() { EchoResponderFactory echoResponderFactory = mock(EchoResponderFactory.class); EchoResponder echoResponder = mock(EchoResponder.class); when(echoResponderFactory.newEchoResponder()).thenReturn(echoResponder);

    EchoServer echoServer = new EchoServer(echoResponderFactory);
    echoServer.serveNext(mock(Socket.class));

    verify(echoResponder).respondTo((Socket) any());
}

@Test(timeout = 100)
public void canRespondToMultipleRequestsSimultaniously() {
    EchoResponder echoResponder = mock(EchoResponder.class);
    EchoResponderFactory echoResponderFactory = mock(EchoResponderFactory.class);
    when(echoResponderFactory.newEchoResponder()).thenReturn(new EchoResponder() {
        @Override public void respondTo(Socket socket) {
            try {
                Thread.sleep(1000);
            } catch(Exception e) {
                throw new IllegalStateException("Interrupted while sleeping");
            }
        }
    }).thenReturn(echoResponder);

    echoServer.serveNext(mock(Socket.class));
    echoServer.serveNext(mock(Socket.class));

    verify(echoResponder).respondTo((Socket) any);
} }</pre>

And here is the corresponding EchoServer:

EchoServer.java<pre> import java.net.*;

public class EchoServer() { private EchoResponderFactory echoResponderFactory;

public EchoServer(EchoResponderFactory echoResponderFactory) {
    this.echoResponderFactory = echoResponderFactory;
}

public void serveNext(Socket socket) {
    EchoResponderThread thread = new EchoResponderThread(socket, echoResponderFactory.newEchoResponder());
    thread.start();
} }</pre>

Next I need a server thread that gets a server socket and makes sure every request is served, and that the server can be run in the background. I need this for the acceptance test.

EchoServerThreadTest.java<pre> import static org.mockito.Mockito.; import org.junit.;

public class EchoServerThreadTest { @Test public void delegatesServingOfClientToEchoServer() { EchoServerThread serverThread = new EchoServerThread(); serverThread.start();

    ServerSocket serverSocket = mock(ServerSocket.class);
    Socket acceptedSocket = mock(Socket.class);
    when(serverSocket.accept()).thenReturn(acceptedSocket);

    EchoServer echoServer = mock(EchoServer.class);

    verify(echoServer).serveNext(acceptedSocket);
} }</pre>

Here I made the first mistake: The server thread should have gotten the server socket, the echo server and a factory object as constructor parameters. I simply forgot this when writing the test down.

Now I can implement the server thread:

EchoServerThread.java<pre> import java.net.*;

public void EchoServerThread extends Thread { public EchoServerThread(ServerSocket serverSocket, EchoServer echoServer) { this.serverSocket = serverSocket; this.echoServer = echoServer; }

private final ServerSocket serverSocket;
private final EchoServer echoServer;

@Override public void run() {
    while(true) {
        echoServer.serveNext(serverSocket.accept());
    }
} }</pre>

Now I can implement the responder. This class does the real work of sending back data to the client.

EchoResponderTest.java<pre> import org.junit.; import static org.mockito.Mockito.; import java.net.; import java.io.;

public class EchoResponderTest { @Test public void canRespondToASingleCharacter() { InputStream inputStream = mock(InputStream.class); when(inputStream.read()).thenReturn(‘a’).thenReturn(-1); OutputStream outputStream = mock(OutputStream.class); Socket socket = mock(Socket.class); when(socket.getInputStream()).thenReturn(inputStream); when(socket.getOutputStream().thenReturn(outputStream));

    EchoResponder echoResponder = new EchoResponder();
    echoResponder.respondTo(socket);

    verify(outputStream).write('a');
}

@Test
public void canRespondToMultipleCharacters() {
    InputStream inputStream = mock(InputStream.class);
    when(inputStream.read()).thenReturn('a').thenReturn('b').thenReturn(-1);
    OutputStream outputStream = mock(OutputStream.class);
    Socket socket = mock(Socket.class);
    when(socket.getInputStream()).thenReturn(inputStream);
    when(socket.getOutputStream()).thenReturn(outputStream);

    EchoResponder echoResponder = new EchoResponder();
    echoResponder.respondTo(socket);

    verify(outputStream).write('a');
    verify(outputStream).write('b');
} }</pre>

The rest of the classes are pretty simple:

EchoResponder.java<pre> import java.net.; import java.io.;

public class EchoResponder { public void respondTo(Socket socket) { try { int readByte; while((readByte = socket.getInputStream().read()) >= 0) { socket.getOutputStream().write(readByte); } } catch(Exception e) { throw new IllegalStateException(“Interrupted while responding”, e); } } }</pre>

EchoResponderFactory.java<pre> public class EchoResponderFactory { public void newEchoResponder() { return new EchoResponder(); } }</pre>

EchoService.java<pre> import java.net.*;

public class EchoService { public static void main(String[] args) { EchoService echoService = new EchoService(); echoService.start();

    Object lock = new Object();
    synchronized(lock) {
        try {
            lock.wait();
        } catch(Exception e) {
            throw new IllegalStateException("Interrupted while waiting", e);
        }
    }
}

public void start() {
    EchoServerThread serverThread = new EchoServerThread(new ServerSocket(7), new EchoResponderFactory());
    serverThread.start();
} }</pre>

Come Back on Friday

Here are the steps I had to take to make the system compile and run:

  • It's public class EchoServerThread, not "void"
  • Another syntax error in the class definition of EchoServer
  • EchoReponderFactory: The factory method can not return null
  • EchoService: The constructor of the server thread needs an EchoServer, which in turn needs the factory
  • There's an unhandled IOException in EchoServerThread ("accept()")
  • There's another unhandled IOExcpetion in EchoService ("new ServerSocket")
  • "import java.net.*" missing in the acceptance test
  • The acceptance test has to write a byte array, not a string
  • Static import for asserts missing in the acceptance test
  • And a lot more imports missing... And some more minor syntax errors...
  • EchoServerThreadTest: Must pass the mocked socket and EchoServer into the constructor of EchoServerThread
  • EchoServerTest: The second test has to create an EchoServer
  • EchoResponderTest: have to cast arguments to "thenReturn" to int
  • Unhandeld exceptions in a couple of tests (Just added "throws Exception")
  • 3 Tests failed! The failures were basically timing issues in the tests that deal with threads. Quickly fixed them by adding sleeps (Dirty workaround, I know).

After I fixed all the compiler problems and the timing issues in the tests, I tried to run the service and telnet to it. It worked!

Did I Learn Something?

  • Must of the issues I had were simple syntax errors
  • A huge mistake I made was the constructor call of EchoServerThread in the test and the main method
  • Another big mistake was that I forgot the IOExceptions in several places
  • Getting all the details right is hard. I mean, hard!
  • Refactoring in your head is even harder. I have extracted some classes, but you can find a lot of duplicate. Especially in the tests.
  • I thought this exercise would be hard. It was even harder
  • Even though I made a lot of mistakes, I did better than I expected.

What do you think of this exercise? Are you thinking about trying it? Contact me and tell me about your experiences. If you blog about this exercise, I will gladly link your blog post here and re-tweet it. All my contact details are at the bottom of the page.