Friday, October 30, 2009

Tests are the Story

I have a recurring discussion with my friend James, regarding test-first development. He believes is that writing tests first is like writing an outline for an academic paper or novel. And sometimes you don't know where the story is going to end up when you start writing. In the past, I've always countered that with the liberal-arts-major argument that you should know what you're going to say before you say it.

However, he makes a good point. Even when you have a goal in mind, sometimes the path to that goal takes unexpected twists and turns. If you write your tests presuming a specific order of steps, they'll become obsolete when the real story doesn't follow the outline.

So here's a different response: the tests are the story. The production code is merely a footnote.

The ultimate goal is satisfied users, and the tests are development standins for those users (even if the "user" is another piece of code). As you write your tests, you should be thinking not of the needs of your mainline code, but the needs of those users. As your understanding of those users changes, your tests will change. Some will be deleted.

You could call this wasted effort, but that's misleading. It was necessary effort, because you didn't have a perfect understanding of the users' needs. The real question is: was it better to explore the plot using tests, or to simply write some mainline code and hope that it did what it should?

Tuesday, October 27, 2009

Tag Reuse in JSPs

One thing that's always puzzled me about JSP taglibs is the mechanics of tag reuse — what happens when you invoke the same tag twice on a page. The Tag JavaDoc says the following:

After the doEndTag invocation, the tag handler is available for further invocations (and it is expected to have retained its properties).

This statement sent up a bunch of red flags when I first read it. And it's done the same for others: I've seen tag implementations that explicitly reset all attributes in doEndTag(). But if this was the required implementation, I'd expect to see it mentioned in the J2EE tutorial or sample code, and it isn't.

Moreover, the description makes no sense: you can't build a software system on components that will render arbitrary values depending on where they're invoked. My first hint came from examining the Java code generated by Tomcat:

private org.apache.jasper.runtime.TagHandlerPool _jspx_tagPool_prodList_form_visible_userId_operation_name_listName_htmlId_format;
private org.apache.jasper.runtime.TagHandlerPool _jspx_tagPool_prodList_form_visible_userId_timeout_operation_name_listName_htmlId_format;

Those are pretty long names. And interestingly, they include the parameters used in the tags. And they're different. That led me back to the JSP specifications, with a more focused search. And on page 2-51, we see the rules:

The JSP container may reuse classic tag handler instances for multiple occurrences of the corresponding custom action, in the same page or in different pages, but only if the same set of attributes are used for all occurrences.

I'd love to know the rationale behind this — and whether it was intentional or a “retro-specification” to cover decisions made in the reference implementation (Tomcat). To me, it seems like premature optimization: assume that it's expensive to instantiate a tag handler or set its attributes, so go to extra effort to pool those instances. If, instead, the specification required handlers to be explicitly instantiated per use it would drive developers toward lightweight handlers that collaborate with external services. Which in my opinion is a far better way to write them.

Monday, October 26, 2009

Unease

If you really don't care you aren't going to know it's wrong. The thought'll never occur to you. The act of pronouncing it wrong's a form of caring.

Zen and the Art of Motorcycle Maintenance is a book that you'll either love or hate. To some, it is a bombastic restatement of ideas that everyone already knows. To others, a lightweight gateway to heavyweight philosophy. And then there are people who believe it changed their life. I can't say that I'm one of the latter, but the book resonates with me on many levels, and I've reread it every few years since I was 15.

I recently paraphrased the above quote in a presentation on testing and coverage. The main point of my presentation was that 100% coverage does not guarantee sufficient testing; an audience member asked the obvious question “then how do you know that you've written enough tests?” My answer can be paraphrased as “you've written enough tests when you feel comfortable that you've written enough.”

Not a terribly good answer, to be honest. It did, however, set up my closing slide, which pointed out that you need committed, test-infected developers to get good tests. If you don't have that, you might as well buy a tool that throws random data at your code. But how does one arrive at the level of caring needed to write good tests? And is it inborn, or something that comes from experience?

I've been thinking about these questions this morning, because I'm refactoring some code and am left with what I consider an ugly method call: if called in one situation I want it to throw an exception, if called in another I want it to fail silently and return null. I've been puzzling over whether I really need to break the method into two parts, and also whether I should think more about another quote from Pirsig:

What's more common is that you feel unpeaceful even if it's right

Sometimes, perhaps, good enough is good enough.