tdd


My colleagues Markus Hjort and Marko Sibakov and I are holding a full-day training session on applying the JDave Behavior-Driven Development (BDD) framework for developing Apache Wicket applications. This will take place in the ApacheCon EU 2009 conference in Amsterdam on Tuesday 24 March from 10 to 17.30 (with lunch break in between).

The intended audience is Java developers that _are_ familiar with Wicket but want to know jdave-wicket, jdave-wicket-webdriver and / or learn how to do automated developer tests^wspecs for Wicket better.

We have posted some online material about the training and there is
a page on the session at the ApacheCon site as well.

Register soon — there’s only room for the first 30 🙂

If you have any questions or comments, please left a comment to the blog or email us directly!

Advertisement

We recently wrote a software using Java 1.4, which prevented us from using some nice developer testing tools that required at least Java 5 (also known as 1.5). Namely these were JDave that would have underlined that we really wanted to do behavior-driven development (BDD), and WicketBenchTestCase of Wicket Bench, which allows easy testing of individual Apache Wicket components.

When discussing this, our coworker Ville remarked that often it makes sense to put the tests of the project in a separate module (something that would be a submodule in Maven 2 multiproject project, and a separate Eclipse project or another module in IntelliJ IDEA). This way it would be trivial to use a different JDK for test and production code. Nevertheless we decided to keep it simple by doing everything in a single Java 1.4 module; the software was small and we felt that the marginal hassle of going from n to n + 1 modules is highest when n == 1.

We wanted to do system testing on a production-resembling platform as soon in the project as possible, but due to various circumstances out of our control, we only got there rather late in the project. And when we finally had deployed the software in the real application server, we were baffled by unexpected errors in some LDAP searches done by our application.

After a quick screening of differencies between our development environments and the system test environment, we tried putting the (proprietary) production JDK also to our development machines. For our great satisfaction, some of the various developer tests (unit + integration tests covering about 90 % of all program code lines, ahem! :)) started failing with the exactly same errors as in the test environment. So this was a case of the JRE abstraction leaking.

Now that the problem could easily be reproduced on any development machine, fixing it was rather straightforward. We even changed our Continuum server to use the production JVM + JRE, and verified that after the fixes, the software was still working as expected on the Java implementation originally used in development.

And then a while later it occurred to me what a good idea, albeit for wrong resons, it had been to write our developer tests on the same Java version as the target environment. If not, reproducing and fixing our bug might have been a lot more difficult.

I am about to publish a detailed hands-on guide for migrating a legacy Enterprise Javabeans (EJB) version 1 or 2 application to a Spring Framework + Hibernate stack. While the guide will concentrate on providing a concrete example of such a migration, I will here give a brief discussion on why and when such a migration would be justified.

For a more authoritative source, I recommend Rod Johnson’s excellent books such as J2EE Design and Development and J2EE Development without EJB. The Spring Framework grew out of Rod’s work outlined in J2EE Design and Development.

Now (in year 2007) everybody agrees that EJB specification versions 1 (from year 1998) and 2 (2001) have inherent problems. It is delightful that EJB 3 (2006) has addressed many shortcomings of the earlier versions, often in ways suggested by leading open source frameworks, though proprietary open source solutions keep on evolving more rapidly and maybe a bit more pragmatically than the standard.

A brief genealogy of the earlier EJB versions can be found in a good post by Floyd Marinescu. The article underlines perhaps the biggest problem of the legacy EJB versions: the EJB 1 specification aims to make every EJB component reusable, even remotely over a network connection. While this provides little value in most enterprise cases, it adds complexity and API clutter. To achieve the questionable goal, EJB components have their lifecycle completely managed by the EJB container, which makes their out-of-container use nearly impossible.

I remember how using EJBs in place of direct RMI seemd to make perfect sense when I was reading Enterprise JavaBeans (third edition) by Richard Monson-Haefel. I had just been working on an RMI project for some months, and like Monson-Haefel, thought I could see how EJBs might have made our project easier. Monson-Haefel’s definition of EJB was

Enterprise JavaBeans is a standard server-side component model for component transaction monitors. (p. 5)

But then I became slightly confused when seeing that in practice, session EJBs were often used on systems that either were or should have been collocated. And entity EJBs seemed stranger still, as the object-relational mapping of container-managed persistence (CMP) was very cumbersome, and bean-managed persistence did not seem to offer any added value over the traditional way of persisting POJOs with JDBC.

The old EJB approach also gave false promises of being able to perfectly automate and abstract some fundamental programming issues, much in line with the RAD tools promises of making programming trivial:

EJB is designed to make it easy for developers to create applications, freeing them from low-level system details of managing transactions, threads, load balancing, and so on. Application developers can concentrate on business logic and leave the details of managing the data processing to the framework.

“A beginner’s guide to Enterprise JavaBeans”, By Mark Johnson, JavaWorld.com, 10/01/98

More problems arise from the classloader scheme of EJB. While good in some cases, in a typical collocated, single-application case, the separation of EJB classes and their transitive dependencies to another classloader invisible to e.g. the web application classloader just makes development-time deployments slower than they would be without EJBs. This reduces developer productivity drastically.

Lately Test-Driven Development has made good progress in making its way to a recommended practice in software development. Old EJB standards have the problem that especially entity beans are impossible to construct outside the container, making unit testing hardly possible. Also using service lookups (from JNDI) instead of dependency injection makes unit testing EJB code complicated; consider

PersonDao mockDao = mock(PersonDao.class);
PersonFinderBean finder = new PersonFinderBean(mockDao);

versus

MockContextFactory.setAsInitial();
Context ctx = new InitialContext();
MockContainer mc = new MockContainer(ctx);
SessionBeanDescriptor dd = new SessionBeanDescriptor("java:comp/env/ejb/PersonDaoEjb",
    PersonDao.class, PersonDao.class, mock(PersonDao.class))) ;
mc.deploy(dd);
PersonFinderBean finder = new PersonFinderBean();

(I adapted the latter example from “Streamlining Your EJB Tests With MockEJB” from BEA dev2dev, by Eoin Woods and Alexander Ananiev 10/17/2005.)

These two kinds of inversion of control, service locators versus dependency injection, are discussed in the classic article by Martin Fowler and EJB 3 has adopted dependency injection in favor of JNDI lookups from client code.

Whereas the new EJB 3 specification fortunately addresses many of the problems of the easier versions, my hunch is that given no political constraints, Spring + Hibernate is still bound to be more productive, because of the greater flexibility and speed of development than EJB. For attaining a more objective view, there are a lot of more or less slanted EJB 3 and Spring comparisons around, such as

Two things are certain:

  1. EJB 1 and 2 have widely acknowledged problems. Whether or not these problems warrant reacting to them depends on the case. A working solution must not be tampered with just for the sake of it. Often it makes sense to improve it in conjunction with other changes, such as delivering new, useful functionality or fixing bugs.
  2. EJB 3 and Spring / Hibernate both provide an approach radically different from the EJB 1 or 2 world. If you suffered with EJB 2, you shouldn’t discard EJB 3 because of that. But it’s always worthwhile to check out proprietary, more independent open source solutions in addition to the standard.

Analysis in philosophy or logic is essentially cutting a conceptual whole to smaller pieces. This is easily connected to agile software development with its hierarchy of a software being analysed to releases, releases analysed to user stories, then to developer stories, and finally to developer tests. (Peter Schrier crystalised this to me in his March 2005 Agile Finland presentation (PDF).)

Software analysed

Robert C. Martin has written a post
“Analysis Vs. Design” where he makes the point that analysis and design in making software are just two different points of view on the same issue. So my word-play of “analytical design” really means exploring this idea in the context of programming (which I believe to be creating the software design). The first developer tests prompted by the tasks at hand can serve as top-level starting points for the analytical design of the actual software component being programmed.

There was also discussion on the TDD Yahoo group in November 2005 on what I find a symptom of this top-down design brought up by TDD. When you start from the top, you easily “bite off more than what you can chew.” When this happens, you must temporarily switch your focus to the smaller detail and test-drive that detail before returning to view the bigger picture. For example, if your task at hand needs a non-trivial String.replaceAll() call involving regular expressions containing metacharacters, you are better off pausing for a while and writing a test that just checks that your replaceAll() call does what you want. This is especially important when you are writing a slow integration test instead of a fast unit test, because if you can test the detail in a fast unit test, you’ll get feedback sooner, and better code coverage by unit tests.

The theme comes up in varying forms, such as the problem of “Mother, May I” tests of Tim Ottinger or Mika Viljanen figuring out what tests to write. In these situations, it clearly helps to make the bootstrap tests as close to the business requirements as possible, and then analyse towards the details. Sven Gorts has written a nice discussion explicitly comparing top-down and bottom-up TDD, and reading it reinforced my opinion that top-down TDD is the way to go.

So to make an example of this, I’m pretending to start to work on a Scrum tool. Let’s imagine that the most critical feature is to see the sprint burndown chart, so I’ll start the implementation with this simple test:

package scrumtool;import junit.framework.TestCase;

public class SprintBurndownTest extends TestCase {
    public void testRemainingIsSumOfRemainingOfTasks() {
        SprintBurndown chart = new SprintBurndown();
        Task t = new Task("Paint the burndown chart", 4);
        chart.add(t);
        assertEquals(4, chart.remaining());
    }
}

This prompts me to create new classes SprintBurnDown and Task, so I’ll do just that. With the quick fix / intention features of the IDE, it’s easy enough to fill in the Task constructor and the add as well as the remaining method of SprintBurndown.

I have a habit (that I believe I got from Jukka) of configuring my IDEs so that every generated method implementation just throws a new UnsupportedOperationException. So the IDE code completion only gets the test to compile, but test execution crashes on the second line on the exception thrown by the Task constructor. For now, I’ll just empty the methods, and put remaining() to return -1 because it needs to return something.

This gets me to this test failure that I wanted:

junit.framework.AssertionFailedError: expected: <4> but was: <-1>

So I make the simplest possible change to make the test pass:

package scrumtool;public class SprintBurndown {

    public void add(Task t) {
    }

    public int remaining() {
        return 4;
    }
}

And ta-da, we’re on green.

Notice that the implementation doesn’t do anything with the Task class. Task was only created because the best bootstrap test case that I came up with needed it. And it should be even more obvious that the current implementation of remaining() will fail miserably in more complex usage scenarios ;), which hints me that I might be correct in wanting a Task concept to help in dealing with that complexity. (Or, I might be mistaken, and I should have started without the Task class, for example just passing Strings and ints to SprintBurnDown.add(). Sorry if this bothers you, but this is the best and most real-world-resembling example that I could come up with.)

Despite good examples, I have not yet learned to thrive for having only one assertion per test, nor to use separate JUnit fixtures efficiently. Rather I want my tests to be good examples of what the code should do. So I will go on making my test method tell more of how the software under test should behave.

public void testRemainingIsSumOfRemainingOfTasks() {
    SprintBurndown chart = new SprintBurndown();
    Task t = new Task("Paint the burndown chart", 4);
    chart.add(t);
    assertEquals(4, chart.remaining());

    Task t2 = new Task("Task can be added to a sprint", 2);
    chart.add(t2);
    assertEquals(4 + 2, chart.remaining());
}

Happily this gives me just the failure I wanted:

junit.framework.AssertionFailedError: expected: <6> but was: <4>

And now to get the test pass, I really feel like I need to make use of Task. I want to add behaviour to Task test-driven; the problem of the burndown chart has been further analysed and we have encountered the Task class.

At this point, it might be a good idea to temporarily comment out the currently failing assertion, as in orthodox TDD there must be only one test failing at a time, and I am just about to write a new failing test for Task.

This is the new test for Task and the implementation that got it to succeed:

// TaskTest.java

package scrumtool;import junit.framework.TestCase;

public class TaskTest extends TestCase {

public void testRemainingIsInitiallyOriginalEstimate() {
        Task t = new Task("Tasks can be filtered by priority", 123);
        assertEquals(123, t.getRemaining());
    }
}

// Task.java

package scrumtool;

public class Task {
private int remaining;

public Task(String name, int estimation) {
        this.remaining = estimation;
    }

public int getRemaining() {
        return remaining;
    }
}

And after this, it was easy enough to make SprintBurndown so that the whole test passes:

package scrumtool;public class SprintBurndown {
private int remaining = 0;

public void add(Task t) {
        remaining  += t.getRemaining();
    }

public int remaining() {
        return remaining;
    }
}

Now the whole test passes! So I’ll clean up the test class a bit.

package scrumtool;import junit.framework.TestCase;

public class SprintBurndownTest extends TestCase {
    private SprintBurndown chart = new SprintBurndown();

    public void testRemainingIsSumOfRemainingOfTasks() {
        addTask("Paint the burndown chart", 4);
        assertEquals(4, chart.remaining());
        addTask("Task can be added to a sprint", 2);
        assertEquals(4 + 2, chart.remaining());
    }

    private void addTask(String name, int estimate) {
        Task t = new Task(name, estimate);
        chart.add(t);
    }
}

In case the point was lost in the midst of the many lines of code produced by a rather simplistic example, here it is again:

  1. Write your first programmer tests as high-level acceptance tests,
  2. and when making them pass, don’t hesitate to step to lower levels of analysis when encountering new non-trivial concepts or functionality that warrant their own tests.

I was very happy to take part in the coding dojo of 8 February 2006. The previous time I had attended was the first public session Helsinki in November, and compared to that, the recent dojo went considerably more smoothly:

  • the cooking stopwatch worked excellently for keeping each person’s turn at ten minutes, with one of the pair rotating every five minutes. (My personal goal for the next dojo is to learn how to set up the watch correctly ;))
  • the audience kept moderately quiet, and the questions were less suggestive than before — i.e. more questions, less directions

And again, I learned valuable things on how other people mould the code, think about the micro-level design, and write tests.

The word “tests” just above should disturb you, if you think that we are practising Test-Driven Development (TDD) in the dojos.

In the randori-style dojo, as a pair produces code, everybody watches it on the projected screen. Sometimes the audience gives slight signals of appraisal, especially when a pair successfully completes a feature, runs the tests, and the xUnit bar turns green. I wanted to cheer not only for the green but also for the red bars. People found this strange, which bothered me, but regretfully I forgot to bring this up in the dojo retrospective. Now I’ll explain why I like the red bar in TDD.

By cheering for the successful red bar, I wanted to underline that making the test fail the right way is clarifying a requirement in an executable form. As I dig deeper into TDD and waddle amidst comments like “I want the tests to drive the design of my application” or “I want my tests to tell stories about my code”, and lately also the new Behaviour-Driven Development (BDD) ideas, I’m staggering towards the comprehension that when doing TDD, we’re not supposed to write tests but to specify requirements.

I’m not sure if Behaviour-Driven Development adds to TDD something more than just the change of the mindset and the vocabulary, but my dojo experience got me thinking that this might be more important than what I had understood. Consider the following (Ruby poker hand evaluator test with Test::Unit):

  def test_four_of_a_kind
    hand = Hand.new ["As", "Ah", "Ad", "Ac", "5s"]
    assert_equal('four of a kind', hand.evaluate)
    hand = Hand.new ["As", "Ah", "Ad", "4c", "5s"]
    assert_not_equal('four of a kind', hand.evaluate)
  end

as opposed to (more or less the same with rSpec):

  def four_of_a_kind
    hand = Hand.new ["As", "Ah", "Ad", "Ac", "5s"]
    hand.should.evaluate_to 'four of a kind'
    hand = Hand.new ["As", "Ah", "Ad", "4c", "5s"]
    hand.should.not.evaluate_to 'four of a kind'
  end

For the record, for this rSpec version to work, I had to add this method to the Hand class:

  def evaluate_to?(hand)
      return evaluate == hand
  end

While Ruby and BDD might or might not be cool, the real point I want to make is that even without the BDD twist, TDD is about design. So what we should practice in a TDD dojo is how to design by writing executable specifications. I think that this is a fascinating, useful and non-trivial skill that is best being rehearsed when working on small and simple examples, such as the tennis scoring and poker hand evaluator which have been the assignments in the Helsinki dojo sessions so far.

Now we have been talking about getting more real-life kind of problems to the coding dojos, so that the participants could learn how to do TDD or at least programmer testing better in an everyday work environment with application servers, databases, networks and whatnot nuisances. Certainly such a hands-on session would accompany well the excellent books on the subject, and help people in adoption of developer testing, but I think that they would be more about the hands-on dependency-breaking or specific technology skills than design.

So although I welcome the idea of exercising in a more realistic setting, I hope that the randoris for doing simple katas will continue as well.

Speaking of dojos, you can now register for the next Helsinki area dojo on Wednesday March 15, 2006 at 18:00-21:00 in Espoo at the premises of Fifth Element. Let’s see if it will turn out more hands-on or micro-level design, but judging from the past experience, at least good time is guaranteed.