Second step

The goal is still the same : showing a simple city map with the main character in the middle. I now have made sure I can answer http calls, thus I’ll pass to declare something about the contents of the answer itself. The simplest version of a city is a building (disclaimer : Christopher Alexander might not agree).

So, what’s a building? A rectangle, we said. But not just any rectangle, it should be shown on a browser, so it’s a rectangle within an html document.

A successful HttpAnswer must thus contain an html document.


@Test
public void shouldAnswerWithAnHtmlDocument() throws Exception {
    new Boss(PORT);
    assertThat(Http.callOn(PORT), is(HttpAnswer.with("<html><body></body></html>")));
}

When I run this test though, I get that PORT is already used. This can mean just one thing, my Boss server from the previous test is still running. I must stop it after the test.
Thus I change the previous test as well, to add a stop operation to Boss.


@Test
public void shouldAnswerHttpCall() throws Exception {
        Boss boss = new Boss(PORT);
        HttpAnswer answer = Http.callOn(PORT);
        boss.stop();
        assertThat(answer, is(HttpAnswer.ok()));
}

Then I implement it in Boss. This should do it :


public class Boss {
    private final SelectorThread selector;

    public Boss(int port) throws IOException, InstantiationException {
        selector = new SelectorThread();
        selector.setPort(port);
        selector.setAdapter(new AlwaysReturn(HttpAnswer.ok()));
        selector.listen();
    }

    public void stop() {
        selector.stopEndpoint();
    }
 }
 

Notice how the Grizzly selector now must be a field of Boss.
The test is still red, but now it’s not due to the need for the port, but because of this :


 Expected: is <org.boss.HttpAnswer@7f60c4b0[statusCode=200,payload=<html><body> </body></html>]>
     got: <org.boss.HttpAnswer@2a114025[statusCode=200,payload=<null>]>

Which is exactly what I need. I’ll make this pass then refactor some more.
To make this test pass the obvious thing to do is to let Boss add the html page to each answer.


public class Boss {
...
    public Boss(int port) throws IOException, InstantiationException {
        selector = new SelectorThread();
        selector.setPort(port);
        HttpAnswer answer = HttpAnswer.with("");
        selector.setAdapter(new AlwaysReturn(answer));
        selector.listen();
    }
...
}

I should not forget to let my little Http utility class parse the payload back (well, if I forgot my test would stay red, so I would know).


public class Http {
...
    public HttpAnswer call() throws IOException {
        Page page = client.getPage("http://localhost:" + port);
        int statusCode = page.getWebResponse().getStatusCode();
        String payload = page.getWebResponse().getContentAsString("UTF-8");
        return new HttpAnswer(statusCode, payload);
    }
...
}

The test is now green, I’m correctly sending back some basic html component.
Yet, there’s a surprise, the first test is not passing anymore. The previous test was expecting an empty http answer with just the success code 200.

Now, I might of course erase the old test, reasoning that it was just a stepping stone to the current test, which is, itself, just there to let me get closer to the goal of my first feature: showing the city map with the character in the middle.

Microdivergence

Yet I won’t. I’ll try to have both tests succeed.

If Boss is the game, then my html declaration is the screen on which the game will appear, and the first test is simply not expecting a screen.


public class BossBehavior {

    private static final int PORT = 11111;

    @Test
    public void shouldAnswerHttpCall() throws Exception {
        Boss boss = new Boss(PORT, new BlankScreen());
        HttpAnswer answer = Http.callOn(PORT);
        boss.stop();
        assertThat(answer, is(HttpAnswer.ok()));
    }

    @Test
    public void shouldAnswerWithAnHtmlDocument() throws Exception {
        Boss boss = new Boss(PORT, new HtmlScreen());
        HttpAnswer answer = Http.callOn(PORT);
        boss.stop();
        assertThat(answer, is(HttpAnswer.with("<html><body></body></html>")));
    }
}

Here it is, a new interface, representing the screen on which game objects will appear. The interface is still quite simple :


public interface Screen {
    String render();
}

And of course the HtmlScreen renders the html and body tags, while the BlankScreen renders nothing.

Now that all tests are green I’ll proceed to some more refactoring.

I don’t like the duplication of the boss.stop() operation I see in the test. I also don’t like my test to concern itself with two different different levels of abstraction : the overall behavior (it returns an html document) and its implementation (the html document is <html><body>…</html>)


public class BossBehavior {

    private static final int PORT = 11111;
    private Boss boss;

    @Test
    public void shouldAnswerHttpCall() throws Exception {
        boss = new Boss(PORT, new BlankScreen());
        assertThat(Http.callOn(PORT), is(HttpAnswer.ok()));
    }

    @Test
    public void shouldAnswerWithTheScreenContents() throws Exception {
        Screen screen = new HtmlScreen();
        boss = new Boss(PORT, screen);
        assertThat(Http.callOn(PORT), is(HttpAnswer.with(screen.render())));
    }

    @After
    public void stopBossServer() {
        boss.stop();
    }
}

Presently I added a new test which is one abstraction level lower than my first test, it’s not concerned about the whole system, just the game general presentation :


public class HtmlScreenBehavior {

    @Test
    public void shouldRenderAnHtmlDocument() {
        assertThat(new HtmlScreen().render(), is("<html><body></body></html>"));
    }

}

Notice how the concept of Screen was born to allow the two tests (“just return the 200 OK” and “return an html document”) to work at the same time.

The force of two similar-yet-diverging tests makes subsystems spring out. Waiting to have two divergent stories is often too late, while very small steps in a single story generate divergence at will.

The movement downwards

Finally, the third test and the Screen interface herald the birth of a gui layer. This is a movement which is very important.

Just as having a first test stating the basic broad facts of a system makes it flexible, using that same level of abstraction to declare all and everything in the system makes the tests rigid and instantly kills the design feedback from tdd.

When the next bit of the story can be declared by using a new, smaller part of the system, we need to focus on declaring it with just that part. If I manage to do it then it means that part has a reason to exist.

There’s something profoundly wrong in a system where the only useful object is the system itself. This is why acceptance tests have little value as a design tool.

There’s also something wrong in skipping the first tests of a new system just because that system happens to be a web application. It generates plenty of urban legends on how tdd works only for the business domain.

By the way, what’s this infamous business domain?

Anyway, let me move on with the next part : showing a building on my screen!

Reality check

My very first tests enforce lots of stuff, but I would really like to see how this system works for real. In case I forgot something.
The quickest way to do it is to make it run. I do this by this very complex method in the Boss class :


public class Boss() {
    public static void main(String... args) throws Exception {
        new Boss(11111, new HtmlScreen());
    }
    ...
}

Then I fire up my browser and type http://localhost:11111/
Surprise! the browser is not parsing the html!

In fact a browser will not parse the html unless the content is declared as being html.
Time to update my tests. I’ll just include the content type in my definition of correct HttpAnswer:


    public static HttpAnswer ok() {
        return new HttpAnswer(200,"","text/html");
    }

Now, when I perform an Http call I’ll also read the content type and use it to initialize the answer. Result, a red test, the very first one :


Expected: is ...HttpAnswer...contentType=text/html]
     got: ...HttpAnswer...contentType=text/plain]

Quick to fix, the HttpAnswer should set the contentType.


public class HttpAnswer{
...
    public void writeTo(Response response) throws IOException {
        response.setStatus(statusCode);
        response.setContentType(contentType);
        ByteChunk chunk = new ByteChunk(payload.length());
        chunk.setBytes(payload.getBytes("UTF-8"),0,payload.length());
        response.doWrite(chunk);
    }
...
}

Test green.
Just a little refactoring to clean this code a bit in order to maintain the same level of abstraction throughout the writeTo method :


    public void writeTo(Response response) throws IOException {
        response.setStatus(statusCode);
        response.setContentType(contentType);
        response.doWrite(payloadAsByteChunk());
    }
Advertisement