Web Apps in TDD, Part 4

Multiple buildings!

I bet that you can tell where this is going, I’ve one building, now I want multiple buildings on my page and then finally put the player in the middle.
So, I add two more rectangles to my investigation.html.


            ...
            var map = new Raphael(0,0,600,400);
            map.rect(10,10,50,40);
            map.rect(80,10,30,40);
            map.rect(10,70,100,40);
            ...

Which produces :

Now, for the test that will lead me to implementing this…


    @Test
    public void shouldRenderMultipleBuildings() throws Exception {
        HtmlScreen htmlScreen = new HtmlScreen();
        htmlScreen.addBuilding(10,10,50,40);
        htmlScreen.addBuilding(80,10,30,40);
        htmlScreen.addBuilding(10,70,100,40);
        User user = new User().lookAt(htmlScreen.render());
        assertThat(user.currentSight(), is("A Rectangle at [10,10], 40px high and 50px wide"));
        assertThat(user.currentSight(), is("A Rectangle at [80,10], 40px high and 30px wide"));
        assertThat(user.currentSight(), is("A Rectangle at [10,70], 40px high and 100px wide"));
    }

The current result is :


Expected: is "A Rectangle at [10,10], 40px high and 50px wide"
     got: "A Rectangle at [10,70], 40px high and 100px wide"

Which is the last building only. This is due to my implementation of the html screen. Which stores the last building and overwrites the previous one. Easy fixed.


    private String renderBuildings() {
        String renderedBuildings = "";
        for (Building building : buildings) {
            renderedBuildings += building.render(vectorGraphics);
        }
        return renderedBuildings;
    }
    
    public Screen addBuilding(int x, int y, int width, int height) {
        buildings.add(new Building(x, y, width, height));
        return this;
    }

function Raphael(x, y, width, height){

    this.rect = function(x, y, width, height) {
        output[invocations++] = "A Rectangle at [" + x + "," + y + "], " +
                    height + "px high and " + width + "px wide";
    }

}

var output = [];

    public String currentSight() {
        return (String) output.get(current++);
    }

Now, for the real-life test


    public static void main(String... args) throws Exception {
        HtmlScreen htmlScreen = new HtmlScreen();
        htmlScreen.addBuilding(10,10,50,40);
        htmlScreen.addBuilding(80,10,30,40);
        htmlScreen.addBuilding(10,70,100,40);
        new Boss(11111, htmlScreen);
    }

But, the very first HtmlScreenBehavior test is not happy, it did expect a rectangle, now that rectangle needs to be added explicitly.


    @Test
    public void shouldRenderABuildingAsARectangle() throws Exception {
        User user = new User().lookAt(new HtmlScreen().addBuilding(10, 10, 50, 40).render());
        assertThat(user.currentSight(), is("A Rectangle at [10,10], 40px high and 50px wide"));
    }

Now it passes. All tests are green.

I’m so glad it’s time to refactor, because my tests are looking very bad. For instance, have a look at the test just after the one I just modified :


    @Test
    public void shouldRenderTheBuildingWithTheRightPositionAndDimensions() {
        User user = new User().lookAt(
        		new HtmlScreen().addBuilding(50, 30, 80, 40).render());
        assertThat(user.currentSight(),
        		is("A Rectangle at [50,30], 40px high and 80px wide"));
    }

Yes, they are the same, the only difference is in the values. This is pretty much the only instance were I do consider erasing a test without a change in features : when it says exactly the same thing as another test.

So, adieu! I delete the second one, as I like the first one’s name better.

What else? Well, I’m growing bored of typing all of these “A Rectangle…”.


    @Test
    public void shouldRenderABuildingAsARectangle() throws Exception {
        User user = new User().lookAt(new HtmlScreen().addBuilding(10, 10, 50, 40).render());
        assertThat(user.currentSight(), is(aRectangle(10, 10, 50, 40)));
    }

    @Test
    public void shouldRenderMultipleBuildings() throws Exception {
        HtmlScreen htmlScreen = new HtmlScreen();
        htmlScreen.addBuilding(10, 10, 50, 40);
        htmlScreen.addBuilding(80, 10, 30, 40);
        htmlScreen.addBuilding(10, 70, 100, 40);
        User user = new User().lookAt(htmlScreen.render());
        assertThat(user.currentSight(), is(aRectangle(10, 10, 50, 40)));
        assertThat(user.currentSight(), is(aRectangle(80, 10, 30, 40)));
        assertThat(user.currentSight(), is(aRectangle(10, 70, 100, 40)));
    }

    private String aRectangle(int x, int y, int width, int height) {
        return "A Rectangle at [" + x + "," + y + "], " + 
        			height + "px high and " + width + "px wide";
    }
Advertisements

Web Apps in TDD, Part 3

Rendering a building

What’s next on the todo list? I want a rectangle to appear on the screen to show a building.
Google tells me there’s a nice javascript library to draw vector graphics on a browser. I think I’ll try it, it’s called Raphael.
So I write an html file using this library to draw a rectangle :


<html>
<head>
    <script type="text/javascript" src="raphael-min.js"></script>
    <script type="text/javascript" charset="utf-8">
        window.onload = function() {
            var map = new Raphael(0,0,600,400);
            var building = map.rect(10,10,50,40);
        };
    </script>
</head>
<body>
</body>
</html>

And with this I get :

A real beauty. The building is the small rectangle top left.

Now I would like my HtmlScreen to return that.

Testing the behavior

I could write a test like this :


    @Test
    public void shouldRenderABuildingAsARectangle() {
        assertThat(new HtmlScreen().render(), is("<html>\n" +
                "<head>\n" +
                "    <script type=\"text/javascript\" src=\"raphael-min.js\"></script>\n" +
                "    <script type=\"text/javascript\" charset=\"utf-8\">\n" +
                "        window.onload = function() {\n" +
                "            var map = new Raphael(0,0,600,400);\n" +
                "            var building = map.rect(10,10,50,40);\n" +
                "        };\n" +
                "    </script>\n" +
                "</head>\n" +
                "<body>\n" +
                "</body>\n" +
                "</html>"));
    }

But it would be awfully fragile. More to the point : it would not declare what I’m interested in.
What is it I’m interested in? If this were a test for some internal functionality it would be easier, you just have to think at what the clients of an object expect from it. Here the client of the rendering from the HtmlScreen is the user : the biped that is looking at the computer screen.

What I would really like to write in my test is :


    @Test
    public void shouldRenderABuildingAsARectangle() throws Exception {
        User user = new User().lookAt(new HtmlScreen().render());
        assertThat(user.currentSight(), is("A Rectangle at [10,10], 40px high and 50px wide"));
    }

Then I think I’ll write exactly this and I’ll find a way to make it work. A solid, meaningful test is definitely worth the effort.

The are three pretty separate issues here :

  • The user should be able to look at the rendering and extract the parts of it which are meaningful for it, right now that means the javascript.
  • The user should be able to evaluate those meaningful parts and get me a short description of the final result.
  • HtmlScreen should return the correct html with the script needed to satisfy the user

Before attacking any of this I put my html inside a string within the test itself and I pass it to the user, I want to make sure that when my user will work I will know.


    @Test
    public void shouldRenderABuildingAsARectangle() throws Exception {
        String html = "<html>" +
                "<head>" +
                "    <script type=\"text/javascript\" src=\"raphael-min.js\"></script>" +
                "    <script type=\"text/javascript\" charset=\"utf-8\">" +
                "        window.load = function() {" +
                "            var map = new Raphael(0,0,600,400);" +
                "            map.rect(10,10,50,40);" +
                "        }" +
                "    </script>" +
                "</head>" +
                "<body>" +
                "</body></html>";
        User user = new User().lookAt(html);
        assertThat(user.currentSight(),
        	is("A Rectangle at [10,10], 50px high and 40px wide"));
    }

Now, on with the implementation of my user.
The first problem is quickly solved, I’ll have the user parse the received html and extract all scripts. The second problem is partially solved by using Rhino to evaluate the scripts.

There’s one thing that hangs : provided I can run the javascript in my user, that javascript does not produce a fancy (and easy to check) text description of what the user sees, it calls Raphael which performs some vector graphics magic.

Luckly, stubbing objects and whole libraries in javascript is trivial. In my tests I’ll replace the Raphael library with this file :


    function Raphael(x, y, width, height){
        this.rect = function(x, y, width, height) {
           output += "A Rectangle at [" + x + "," + y + "], " + height + "px high and " + width + "px wide";
        }
    }
    
    var output = "";

I run this and the test is still red, Rhino says that there’s no window object available. Indeed the window object is provided by the browser. I’ll just make sure that my user executes a browser.js file before everything else when evaluating the HtmlScreen :


    function Window() {}
    var window = new Window();

The test is green.

I move the html to HtmlScreen, this test is green but the old one is red, remember the first test for HtmlScreen?


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

That’s definitely not true any longer. Is interpolating the microdivergence between these two tests going to help my design? Maybe, but I’ve added a lot for this last test, I don’t want to follow up with further changes in the production code just to satisfy that old boring test. Moreover, the reason that test is failing is that it is extremely dependent on the actual html string.

That’s not really good, especially since I’ve done so much to avoid being dependent to the html string in my latest test. Finally, I’ve just added a dom library for use inside the user, it’s really quick to change that test to declaring the same thing without being this fragile :


    @Test
    public void shouldRenderAnHtmlDocument() throws Exception {
        Document document = new Builder().build(new HtmlScreen().render(), null);
        Element root = document.getRootElement();
        assertThat(root.getLocalName(), is("html"));
        assertThat(root.getChildElements("body").size(), is(1));
    }

Is it me, or there’s still no really simple xml library for Java? This is xom, I decided to try it after years of jdom, dom4j and the basic java dom (ugh!) : it has a simple builder, good; poor navigation, bad; and it forces me to pass a null in the builder since I have no base url, very bad.

Anyway, the HtmlScreenBehavior tests are all green. I’ve one red test, it’s BossBehavior. It complains that raphael does not exist.
In fact the WebClient performing the http call is parsing the html from the server response and it is calling the server to obtain the raphael-min.js. My server does not currently return static resources.
Even if I have a red test I add a new one into BossBehavior, which is far more focused, that will ensure a clear feedback and also prove my theory on the origin of the error.


    @Test
    public void shouldReturnAStaticFileWhenAPathIsProvided() throws Exception {
        boss = new Boss(PORT, new BlankScreen());
        assertThat(Http.callOn(PORT, "sample.content.txt"),
		is(HttpAnswer.with("foo content bar").type("text/plain")));
    }

Inside the sample.content.txt file I got the string “foo content bar”.

Indeed the test fails, as expected.

Grizzly has a nifty Adapter ready for this, the StaticResourcesAdapter, but, how to choose between my original adapter and this one? Well, when a resource is required I’ll use the StaticResourcesAdapter, when no resource is requested it will be my original adapter.
Doesn’t this sound a lot like mapping?


public class AlwaysReturn{
	...
        public void service(Request request, Response response) throws Exception {
            String path = request.unparsedURI().toString();
            if(path != null && !path.equals("/")) {
                filesRetriever.service(request, response);
            } else {
                HttpAnswer.with(screen.render()).writeTo(response);
            }
        }
        ...
}

The “filesRetriever” is the static resources adapter from Grizzly.

I run all tests and they are all green!

I perform a reality check, I launch Boss with its main and I call it with a browser.

And here it is, the first building of my city.

Yet looking back at the last piece of code I wrote, I don’t like what I see. Simply put, the service method is a mess : it is performing operations from multiple levels of abstraction, it is meddling with the static adapter while it is also performing actions which are specific to the screen. Finally it belongs to a class “AlwaysReturn” whose name has lost all meaning.
I’ll clean this in a pretty direct way, for now.


    private static class RootAdapter implements Adapter {
        private final StaticResourcesAdapter resourcesAdapter;
        private final ScreenAdapter screenAdapter;


        public RootAdapter(StaticResourcesAdapter resourcesAdapter,
        				ScreenAdapter screenAdapter) {
            this.screenAdapter = screenAdapter;
            this.resourcesAdapter = resourcesAdapter;
        }

        public void service(Request request, Response response) throws Exception {
            if(pathIsPresentIn(request)) {
                resourcesAdapter.service(request, response);
            } else {
                screenAdapter.service(response);
            }
        }
        ...
    }

Tests are still green.

Controlling the building

I’ve now the render of a building, but that render is static, I can’t really control it in any way.
Thus I’ll add a new test :


    @Test
    public void shouldRenderTheBuildingWithTheRightPositionAndDimensions() {
        User user = new User().lookAt(new HtmlScreen().addBuilding(50,30,40,80).render());
        assertThat(user.currentSight(), is("A Rectangle at [50,30], 40px high and 80px wide"));
    }

The test does not compile, I need the method “addBuilding”, easy added (empty, of course). Test is now failing, I always get the old rectangle in the old position. Fine.

This code makes all tests pass :


public class HtmlScreen implements Screen {
    private Building building = new Building(10, 10, 40, 50);

    public String render() {
        String start = "<html><head>" +
                "    <script type=\"text/javascript\" src=\"raphael-min.js\"></script>" +
                "    <script type=\"text/javascript\" charset=\"utf-8\">" +
                "        window.onload = function() {" +
                "            var map = new Raphael(0,0,600,400);";
        String end = "}</script></head><body></body></html>";
        return start + building.render() + end;
    }

    public Screen addBuilding(int x, int y, int height, int width) {
        building = new Building(x, y, height, width);
        return this;
    }
}

This code is ugly. The HtmlScreen is assembling the overall html, importing Raphael and composing the script. Building.render() is also strongly coupled with Raphael. It is also coupled with the fact that the Raphael canvas is named “map”, see for yourself :


    public String render() {
        return "map.rect(" + x + ","+ y + "," + width + "," + height + ");";
    }

This can’t do. I want everything related to Raphael to stay isolated and I want the HtmlScreen to focus on assembling the overall html, not the details of the script. I think I need a VectorGraphics object.


public class HtmlScreen {
    ...
    public String render() {
        return header() +
                vectorGraphics.include() +
                openScript() +
                openFunction() +
                vectorGraphics.init() +
                building.render(vectorGraphics) +
                closeFunction() +
                closeScript() +
                ending();
    }
    ...
}

public class VectorGraphics {

    public String include() {
        return "<script type=\"text/javascript\" src=\"raphael-min.js\"></script>";
    }
    
    public String init() {
        return "var map = new Raphael(0,0,600,400);";
    }

    public String rect(int x, int y, int width, int height) {
        return "map.rect(" + x + ","+ y + "," + width + "," + height + ");";
    }
}

public class Building {
    ...
    public String render(VectorGraphics vectorGraphics) {
        return vectorGraphics.rect(x, y, width, height);
    }
    ...
}

I don’t know if this idea of a vector graphics renderer, used by the building and by the HtmlScreen as well, represents a new and lower level of abstraction. In theory it is, as it manages every detail of the use of javascript for graphics, but strong abstractions aren’t always the most obvious ones, only tests will tell.

Web Apps in TDD, Part 2

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());
    }