Test Driven Development Get Started

The start is always the hard part. Most often we face existing code where we have to add some new functionality. In always all cases there are little to no unit tests, but ugly and complicated looking integration or system tests which sometimes fail because of timing, network, or database issues. If you have already had a huge pile of unit tests in place, then you can stop reading. For the rest of us, proceed.

The Greeting App

Let's dive into a simple example to illustrate how to get out of the mess. We have a very simple say hello app

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

Just something which says hello. Simple. Can you test that? Well yes, you can start it and see if it says "Hello Tom" if you enter "Tom". Maybe there is a shell script based system test which tests that the app says hello to everyone. So whenever you improved your code you would have to start the system tests afterward. Nothing wrong with that. But it is slow to start the Java app and depending on what else you need, like a database or a REST call, you would also have to maintain the surrounding stuff with test data. Network and database add more failure points to the tests. The network could be down, the database could be in a funny state because your workmate did change something for his new feature and deleted all your test users. So system tests tend to be a fragile. I use system tests as well, but as simple as possible and as few as possible.

Refactor It to Make It Testable

To be able to test that with unit tests we have to refactor this class and put the greeting part into a class. Like this

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

And the main class would then just call this Greeter to say hello.

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

So you extracted the functionality into a class that you can test. That's the way to go to have unit tests. This also helps you on a micro level to organize your code in a reusable way.

Unit Test

And the unit test for the Greeter would look like this

public class GretterTest {
    @Test
    void shouldGreetTom() {
        assertEquals("Hello Tom", new Gretter().sayHelloTo("Tom"));
    }

void shouldGreetJerry() {
        assertEquals("Hello Jerry", new Gretter().sayHelloTo("Jerry"));
    }
}

Yes, I have two tests. If I do that test driven I would start with a null to make the first test fail, then I would write a fixed string to greet Tom and then I would add the Jerry test to replace the fixed string. But more on that in the next blog post.

System Test

So now we are on a level where we can write unit tests for this greeting software. We will have one simple system test in place as well, just to see if it says something with Tom in it. We are only interested in the keyboard input is proper. Could look like this

#!/bin/bash
java -jar greetingApp.jar < Tom | grep "Tom"
exit $?

If the keyboard input does not work it will fail as the grep would return something different than 0. Why didn't I just write this system test you might ask? Because it is just a matter of time when your customer wants that different. They might want the software to be aware of the time and greet you with "Good morning Tom", "Good afternoon Tom", "Good evening Tom" stead just "Hello Tom". So I only test the input of "Tom" that's it, the edge cases I try to do on the lowest level possible and not on the highest level. It keeps your tests stable and very very fast.

The End

For now, I already prepared the next blog post where I introduce you to the test driven development mantra. Follow me on twitter @ia97lies and on @artificials_ch. Leave a comment below.

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.