DEV Community

Emmanuel Genard
Emmanuel Genard

Posted on • Originally published at emmanuelgenard.com on

Chapter 2: Degenerate Objects

TDD by Example: Chapter 2 Degenerate Objects

Summary

The chapter starts off with a summary of the "general TDD cycle". My summary of that summary is:

  1. Write a test so your code has the API you want it to have.
  2. Get the test to green as fast as possible. Don't worry about "clean code" or "design".
  3. Improve the design of the code while keeping the tests green. Otherwise known as refactoring.

He explains that the idea behind these three steps is to reduce the cognitive load on the programmer. First get the code to work, then worry about how it's designed. This approach is in contrast to what he calls "architecture-driven development" which focuses on the design of the code first and then tries to get it to work.

He puts the ideas into practice using them to remove the side effects from the Dollar object that mutates the value of the amount property. He writes a test that passes if multiplying a Dollar object returned a new instance of Dollar whose amount is the result of the multiplication.

He changes the test from this:

public void testMultiplication() {
    Dollar five = new Dollar(5);
    five.times(2);
    assertEquals(10, five.amount);
    five.times(3);
    assertEquals(15, five.amount);
}

To this:

public void testMultiplication() {
    Dollar five = new Dollar(5);
    Dollar product = five.times(2);
    assertEquals(10, product.amount);
    product = five.times(3);
    assertEquals(15, product.amount);
}

He then introduces two tactics for quickly getting the tests to pass, hardcoding and implementation and doing the obvious thing. Hardcoding an implementation means that you write something to just get the tests to green. You then refactor the code into a real implementation while keeping the tests green. Doing the obvious thing is used when the real implementation is obvious to you. These are the two main tactics used by Beck. He mentions a third tactic, Triangulation, that will be explained in the following chapter.

The chapter ends with a paragraph talking about using your feelings to guide you. Beck used the feeling of something being wrong with the code to write a test that exposes the underlying reason for the feeling. Getting the test to pass made him feel better and improved the design.

Commentary

There is one great thing about this chapter and two problematic things.

I think the restatement of the TDD cycle and the example that followed is great. Repetition of the steps of TDD in a different way helped my understanding. I hope he does this many more times throughout the book.

The goal is clean code that works (thanks to Ron Jeffries for this pithy summary). Clean code that works is out of the reach of even the best programmers some of the time, and out of the reach of most programmers (like me) most of the time. Divide and conquer, baby. First we'll solve the “that works” part of the problem. Then we'll solve the “clean code” part.

I think this quote captures the power of TDD as a design tool. By writing the test, getting it to past and then worrying about the design of the code, you can be sure that your design works as longs the tests still pass.

The problems start right after:

This is the opposite of architecture-driven development, where you solve “clean code” first, then scramble around trying to integrate into the design the things you learn as you solve the “that works” problem.

Sometimes you need to think about the problem before writing any code. And one of the best tools for thinking is drawing. It externalizes your thoughts and helps you see the problem at a different level of abstraction. It is also faster feedback than writing code. I don't know if drawing a UML diagram or any other type of diagram is what Beck means by "architecture-driven development". He doesn't explain it. He just takes a passing shot.

In my experience, if I can represent the problem or different solutions to the problem at a different level of abstraction this can lead to insights that are difficult to see when down in the code. A diagram can help answer the question, "What tests should I write?" or "Where should I start?". It's also much cheaper to explore different approaches with drawings than with code. You can just erase a whiteboard or throw out a piece of paper.

The other thing I have a problem is this:

The translation of a feeling (for example, disgust at side effects) into a test (for example, multiply the same Dollar twice) is a common theme of TDD.

When he was writing this book Beck was already an expert at programming and at TDD. He had been doing both long enough to have built up an intuition. He could feel when something was wrong. A person just learning will not have that intuition. I think it would have been a more useful for a wider range of people if he took the time to point out exactly what was wrong with the design. Implying that anyone reading this book can go off a feeling, especially the same feelings he has, is something that can alienate the readers.

Top comments (0)