Test Driven Development Mantra

This is the mantra

  • Think of a piece of code you want to add
  • Write a test which fails because that piece of code is not in place
  • Write that piece of code to make the failing test succeed
  • Refactor the code
  • Refactor the test

and you do that over and over again. Why did I not start with writing a test which fails? Because if you think for the next test without having code in your mind it's incredible hard to figure out especially in the begining. This first step is a help and that works surprisingly well for me and for the people I taught test driven development with my workshops. It also keeps the steps as small as possible and in my opinion, that's key if you want to test all the aspects of your new class.
And I do refactor the test as well and organize that around stories mostly referred to as fixtures. I will write about fixtures in one of the next blog posts about test driven development.

Simple App

Let's carry on with the greeter app from the last blog post

Gretter.java

public class Greeter {
    public String sayHelloTo(String name) {
        return "Hello " + name;
    }
}

Hello.java

class Hello {
    void main() {
        Scanner input = new Scanner(System.in);
        String name = input.next();
        System.out.println(new Greeter().sayHelloTo(name));
    }
}

On the last meeting with the customer, we agreed that our greeting app must greet different depending on the daytime. Or more precisely our greeting app must say "Good morning" after 12 am, "Good afternoon" after 12 am and "Good evening" after 6 pm.

Practice

I will guide you to know how to write the needed code test driven step by step.

First Failing Test

What could be the next line of code we want to write? The easiest way ist to have an additional method in the Greeter class like this

public String sayHelloTo(String name, DateTime time) {
    return null;
}

We don't write this, it's just that we think of it. That is the first step. But wait... how does the failing test look like if that code is not even in place? Well it doesn't compile and that is the failing test and it looks like

public class GreeterTest {

    @Test
    void shouldSayGoodMorningToTom() {
        sayHelloTo("Tom", new DateTime());
    }
}

Make the Test Happy

To make it compile we simply add the piece of code we thought about above and it compiles and even works.

Skip Refactor The Code and The Test

In this case, we don't need to refactor the code or the test as there is nothing to improve so far. We skip these two steps in the mantra consciously.

Next Piece Of Code

The next piece of code is probably something like this

    public String sayHelloTo(String name, DateTime time) {
        return "Good morning " + name;
    }

but... resist writing the code!

Let's Think of The Next Test

What could be the next test which fails? Exactly we add now an assertion for this. And here it goes

    @Mock
    private DateTime dateTime
    @Test
    void shouldSayGoodMorningToTom() {
        when(dateTime.getHourOfDay()).thenReturn(6);
        assertEquals("Good morning Tom", sayHelloTo("Tom", new DateTime()));
    }

I made already all the proper assumptions to say good morning and mocked the DateTime to return 6 in the morning if getHourOfDay is called.

And it will fail because our code just returns null.

Make The Second Test Happy Too

This is now really important and it is the only way to test all edge and aspects of your application in a test driven way. What do you think is the minimal code you need to make the app say "Good morning Tom"? What if I say just return exactly that? I mean literally this static string?

    public String sayHelloTo(String name, DateTime time) {
        return "Good morning Tom";
    }

I give you some time to digest... and while digesting run the test to see it is green. I mean it! Hit the damn button and run it.

And yes I know it is not our initial piece of code we thought of, but that is ok because that only helps us to find the next failing test. It's a help and actually optional if you can do that without, I can not.

What Next?

The first thing we want to go rid of is this super static string. I guess that is your inner urge. Well then let's think of a less static string, like this

    public String sayHelloTo(String name, DateTime time) {
        return "Good morning " + name;
    }

But... don't write this code, just think of this code, because we need first a failing test!

Let it Fail Again

But how can we make the test fail? We simply write a new test with a different name, boom!

    @Test
    void shouldSayGoodMorningToJerry() {
        when(dateTime.getHourOfDay().thenReturn(6);
        assertEquals("Good morning Jerry", sayHelloTo("Jerry", new DateTime()));
    }

Isn't that amazing? So simple to think of the next failing test if you keep thinking of a small portion of code.

That's It For Now

What are the next steps for daytime addition to making the app say "Good afternoon Tom" if the getHourDay() method returns 14?

Write in the comment about how you would do that. What is the code you think of and what is the test which fails because that piece of code is not yet there? And finally what is the code to make the test happy.

Christian Liesch

Software Developer

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.