The Test-First Stoplight

Extreme Programmers write tests before they write the corresponding production code. In “test-first programming,” you start with a small test, then write just enough code to implement it, and continue with the next test until the code is done.

The Test-First Stoplight

Test-first programming is like a stoplight. A normal stoplight has green, yellow, and red lights. It starts green, goes yellow, then red, and back to green again. Occasionally, you come across a light where the pattern is different, perhaps blinking yellow or blinking red. When you see this, you pay extra attention, and say “What’s going on?”

For unit testing in Java, many projects use the JUnit testing framework, available at www.junit.org. It shows a green bar when all tests pass, and a red bar when any test fails. We’ll think of those as green and red lights on a stoplight; failing to compile will be a yellow light. Just like a real stoplight, we expect to go green, yellow, red; green, yellow, red.

  1. Start. (Green light!)
  2. Write a test.
  3. Try to run the test.
    It fails to compile, because the called routine hasn’t been written yet. (Yellow light!)
  4. Write a stub for the new routine.
  5. Try to run the test.
    It fails, because the stub doesn’t do anything yet. (Red light!)
  6. Write the body of the stubbed routine.
  7. Try to run the test.
    It passes. (Green light again!)
  8. Start the cycle again.

Abnormal Patterns

From green to green: The test passed right away.
Either the test is bad, or you’ve already implemented the feature being tested. You might consider modifying the implementation just to see the test fail. (But see the “Refactoring” section below.)

From green to red: The test failed right away.
This is OK if it’s a new test of an existing routine.

From yellow to yellow: Oops – syntax error creating the stub.

From yellow to green: The test didn’t compile without the stub, but adding the stub let the test pass.
This is very suspicious: if a do-nothing stub makes the test work, is the test valid?

From red to yellow: Oops – syntax error implementing the routine.

From red to red: New code didn’t work.
This happens to everyone occasionally – just fix the code.
But – if it happens a lot, it’s telling you to move to smaller tests (so you’ll add smaller bits of new code as well).
 

Quick Cycle

The cycle doesn’t take very long:

  • write test, stub, and body
  • compile three times
  • run twice

Depending on your environment, one to five minutes might be a typically time to run through the cycle. If you find yourself spending 10 to 15 minutes or longer, the cycle is too long, and you need to move to smaller tests.
 

Refactoring

When you’re refactoring, you’re often not seeing the stoplight go yellow and red. It’s more like those days when you drive down the road and hit every light green.

  1. Start. (Green light.)
  2. Apply the refactoring.
  3. Compile and run the test. (Green light again!)

You will get the occasional yellow or red light, telling you to watch your syntax, or be more careful in your transformations, or because the refactoring uses the compiler to tell what’s safe. But mostly, it’s green lights all the way.
 

Example

Let’s see a small example of test-first programming. Suppose you have a person object like this:

public class Person {
    String name;
    int favorite = -1;

    public Person (String name, int favorite) {
        this.name = name;
        this.favorite = favorite;
    }
}

You’d like to represent this as an XML string like this:
    <name>the-name</name>
(if favorite is less than 0), or
    <name favorite=”nn”>the-name</name>
if favorite is 0 or more.
 

  • Start with a green light.
  • Create a test
        Person p = new Person("Pop", -1);
        assertEquals("<name>Pop</name>", p.asXml());
  • When you compile, you get a syntax error (yellow light!) because the asXml() method doesn’t exist.
  • Stub out the method on Person:
        public String asXml() {return null;}
  • Compile and run; the test fails (red light!).
  • Implement the method as simply as possible:
        public String asXml() {return "<name>" + name + "</name>";}
  • Compile and run – green light! – a full cycle.
  • We can add a second test, and see another situation:
        Person p2 = new Person("Mom", 1);
        assertEquals("<name favorite="1">Mom</name>", p2.asXml());
  • When we compile and run, we skip the yellow light, and get a red light. Why? Because asXml() exists but doesn’t have the added functionality.
  • Implement the extra code, and get a green light again.

Conclusion

Visualize the stoplight as you code, until any abnormal pattern gives you just a little twinge.

Related Articles

Translations

  • Spanish. Courtesy of Diego Santa.

[Written 1-2-2001, William C. Wake]