Developers, please write tests! I know it is hard but… wait, hard? No it isn’t! Really! The most problem of writing tests is that most people write tests after the implementation. This is hard! But no worry, there is a solution. Test-driven development (short TDD).
But what does it mean? It’s simple. Before you implement any implementation (or creating classes) write the test. And yes, when I say before creating class I mean it.
So let’s start with a simple example:
You need to implement a simple clock. You can ask the clock what time is it and change the output style in four different ways:
German time: 14:45 Uhr
American time: 2.45 PM
German time with date: 14:45 Uhr, Freitag 12.02.2016
American time with date: 2.45 PM, Friday 12.02.2016
First thing is to implement a clock object. But first — think of TDD — a clock test object.
Now you can write your test. The first test is to ask the time.
It’s absolutely OK to create an instance — in your tests — without having any concrecte object. Get help from your IDE and let it create the class for you.
It is the same in any other things you do in your test. Let’s take a look in calling methods:
But back to the test. Notice the method name. It’s currently a bad name. And this is OK, too. Because you don’t actually know what you want to test. So, wait and name the test later.
Your first test, for the time in a german time format, should look like this
@Test
public void testName() throws Exception {
MyClock clock = new MyClock();
String germanTime = clock.getCurrentTime();
assertThat(germanTime, is(equalTo("14:45 Uhr")));
}
And now? Now we have a name for the test because we know what they do. We test getCurrentTime() which should return the time in german format. So a good test name for that is the following I think:
testGetCurrentName_ShouldReturnTimeInGermanFormat()
Now write the implementation to make the test green. That’s the code:
public String getCurrentTime() {
return "14:45 Uhr";
}
Cool, let’s make a second test. The test is similar to the first, but now we want an american time format (the different ist here only the assertThat()):
@Test
public void testGetCurrentName_ShouldReturnTimeInAmericanFormat() throws Exception {
MyClock clock = new MyClock();
String americanTime = clock.getCurrentTime();
assertThat(americanTime, is(equalTo("2:45 PM")));
}
Results in:
java.lang.AssertionError:
Expected: is “2:45 PM”
but: was “14:45 Uhr”
So how we can solve this without to break the first test? I think a good solution is a parameter which handels the return value. Let’s create an enum and passing in getCurrentTime().
The enum class:
public enum TimeFormat {
GERMAN, AMERICAN
}
Tests:
@Test
public void testGetCurrentName_ShouldReturnTimeInGermanFormat() throws Exception {
MyClock clock = new MyClock();
String germanTime = clock.getCurrentTime(TimeFormat.GERMAN);
assertThat(germanTime, is(equalTo("14:45 Uhr")));
}
@Test
public void testGetCurrentName_ShouldReturnTimeInAmericanFormat() throws Exception {
MyClock clock = new MyClock();
String americanTime = clock.getCurrentTime(TimeFormat.AMERICAN);
assertThat(americanTime, is(equalTo("2:45 PM")));
}
And our implementation:
public String getCurrentTime(TimeFormat format) {
switch (format) {
case GERMAN:
return "14:45 Uhr";
case AMERICAN:
return "2:45 PM";
default:
return "";
}
}
Now all tests are green. It’s time to refactore if possible. Both — the test and the implementation. The only thing we can do here is to move the MyClock instance in a setUp() method:
private MyClock mMyClock;
@Before
public void setUp() throws Exception {
mMyClock = new MyClock();
}
The rest — the output with date — is the same like above. The full code for that can be found in this GitHub Gist.
And yes, that’s all. In the next article I’ll add more functionality to the clock. Because a clock with a static time is very useless.