TDD First Test – Is Creating the Objects You Think You Will Need OK in a First Test in TDD?

extreme programmingjavatddtesting

I'm fairly new to TDD and I have trouble when creating my first test when it comes before any of the implementation code. Without any framework to the implementation code I am free to write my first test however I want but it always seems to come out tainted by my Java / OO way of thinking about the problem.

For example in my Github ConwaysGameOfLifeExample the first test I wrote (rule1_zeroNeighbours) I started out by creating a GameOfLife object that hadn't been implemented yet; called a set method that didn't exists, a step method that didn't exist, a get method that didn't exist, and then used an assert.

The tests evolved as I wrote more tests and refactored, but originally it looked something like this:

@Test
public void rule1_zeroNeighbours()
{
    GameOfLife gameOfLife = new GameOfLife();
    gameOfLife.set(1, 1, true);
    gameOfLife.step();
    assertEquals(false, gameOfLife.get(1, 1));
}

This felt odd as I was forcing the design of the implementation based on how I had decided at this early stage to write this first test.

In the way that you understand TDD is this ok? I seem to be following the TDD / XP principles in that my tests and implementation evolved over time with refactoring, and so if this initial design had proved useless it would have been open to change, but it feels like I am forcing a direction on the solution by starting this way.

How else do people use TDD? I could have gone through more iteration of refactoring by starting out with no GameOfLife object, only primitives and static methods but that seems too contrived.

Best Answer

This felt odd as I was forcing the design of the implementation based on how I had decided at this early stage to write this first test.

I think this is the key point in your question, Whether or not this is desirable depends on whether you lean towards codeninja's idea that you should design up-front then use TDD to fill in the implementation, or durron's idea that tests should be involved in driving out the design as well as the implementation.

I think which of these you prefer (or where you fall in the middle) is something you need to discover for yourself as a preference. It's useful to understand the pros and cons of each approach. There are probably plenty, but I'd say the main ones are:

Pro Upfront Design

  • However good a process TDD is at driving design, it's not perfect. TDing without a concrete destination in mind can sometimes run down dead ends, and at least some of these dead ends could have been avoided with a bit of upfront thinking about where you want to end up. This blog article makes this argument using the example of the Roman Numerals kata, and has a rather nice final implementation to show for it.

Pro Test-Driving Design

  • By building your implementation around a client of your code (your tests), you get YAGNI-adherence pretty much for free, as long as you don't start writing unneeded test cases. More generally, you get an API which is designed around its use by a consumer, which is ultimately what you want.

  • The idea of drawing a bunch of UML diagrams before writing any code then just filling in the gaps is nice, but rarely realistic. In Steve McConnell's Code Complete, design is famously described as a "wicked problem"- a problem you can't fully understand without first at least partially solving it. Couple this with the fact that the underlying problem itself may change through changing requirements, and this model of design starts to feel a bit hopeless. Test driving allows you to just bite off one chunk of work at a time- in design, not just implementation- and know that at least for the lifespan of turning red to green, that task will still be up to date and relevant.

As for your particular example, as durron says, if you did go with an approach of driving out the design by writing the simplest test, consuming the minimal interface it can, you probably would start with a simpler interface than the one in your code snippet.