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.

Advertisements

2 thoughts on “Web Apps in TDD, Part 3

  1. You say “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.”

    Oh, not so trivial problems for me: I’d like to have a look to the User class ;-)

    Thanks

    Franco

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s