Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Tomek Kaczanowski - Practical Unit Testing with JUnit and Mockito - 2013.pdf
Скачиваний:
228
Добавлен:
07.03.2016
Размер:
6.59 Mб
Скачать

Chapter 4. Test Driven Development

probably have the final answer to whether my idea is actually worth money by now. Instead, all I have is a complicated test that doesn’t work, a pile of frustration, [and] eight fewer hours in my life […].

— Kent Beck Just Ship, Baby (2009)

Writing tests usually requires a good understanding of the technologies used, and knowledge of the problem domain. If you lack these, you will probably be better off starting with the code-first approach - especially if you lack the time to acquire the necessary knowledge and know you will spend time struggling with the (unknown) testing framework instead of writing real code. As usual, use common sense and learn from your mistakes.

Another TDD stopper is when you apply it to some legacy code. You might not be able to go with TDD without some serious refactoring - thus, hitting the "chicken and egg" problem, because you cannot refactor for real without a safety net of tests… But then you’ll have a few dirty tricks up your sleeves: tools which allow you to deal with all the weirdness of your legacy code10. Sadly, there will be a time when you will need to use them, even if you would gladly rewrite the whole code from the scratch.

However, all this applies more to integration tests than unit tests. My experience with test-first coding of unit tests is that it is always possible to do at least some state testing this way. As for testing of interactions between objects, this is sometimes significantly harder (because of some legacy non-easily- testable code structure).

4.9. Should I Follow It Blindly?

I am not apt to follow blindly the lead of other men.

— Charles Darwin

TDD is based on a set of very simple rules: first write a test, then see it fail, make it pass, refactor, see it is still green, and then… repeat the cycle. Moreover, their beauty is magnified by their wide scope of applicability – you can tackle almost any programming dilemma with them. However, these rules are not carved in stone. You will encounter some situations where it make sense to omit some steps. This section gives some examples of such situations.

4.9.1. Write Good Assertion Messages from the Beginning

After you have gained some experience with JUnit you will just know when the default assertion message is good enough, and when it should be corrected. I do not see any point in waiting for the test to fail, only to see that my experience was telling me the truth and that I should, indeed, fix the message.

That is why I often write good assertion messages without waiting for the test to fail.

4.9.2. If the Test Passes "By Default"

IDEs are great. However, they can interfere with your TDD habits. Sometimes they will not let you see a failing test, by auto-generating the required functionality. Let us have a look at an example. Suppose you start writing a test, as shown in Listing 4.18.

10This takes us back to the distinction between "tools for verification" and "tools for design" discussed in Section 1.3.

59

Chapter 4. Test Driven Development

This might not be the best possible test, but it is good enough to illustrate the case in point. When writing it, your IDE will probably suggest auto-generating an empty implementation of the required methods in order to make the test compile.

Listing 4.18. A simple getter/setter test

Client client = new Client();

client.setAge(20);

assertEquals(20, client.getAge())

First the IDE will suggest creating a client class with a default constructor – and this is good! When it comes to the setAge() and getAge() methods, IntelliJ IDEA will offer two options: to create a method, or create a getter/setter.

If you choose the second option – generating getters and setters – IntelliJ IDEA will generate the following code:

Listing 4.19. Code autogenerated by IDE

public class Client { private int age;

public void setAge(int age) { this.age = age;

}

public int getAge() { return age;

}

}

Now, if you run the test, you will see it pass. Oops… seems like we have just skipped the first step of TDD! This code will not only compile, but pass the first test, so you have no chance to see the failing test!

In the case of such trivial code, I would suggest dropping your TDD habits for a minute and believing that IDE is capable of doing things correctly. However, there is one thing that we have to do. If the test fails, we will be 100% sure it was executed. But if all we see is a green color, then we have to be certain about whether it was executed at all. In cases of larger test suits it might be easy to overlook a test that was not run. So at least make sure your test was actually executed.

Unfortunately, there are some subtler cases, where extra care is required. Remember the tests of the compareTo() method that we created for the FootballTeam class (see Section 4.5.6)? The default return value which the IDE put into the compareTo() method was 0. Given that, if we were to compare two football teams with equal numbers of wins, such a test would pass instantly. The code of the compareTo() method is much more complicated than the getters/setters discussed previously, and I would not recommend taking it for granted that "everything is just fine". Instead, I would suggest the following: break the code so you see the test fail.

In our case, that would mean changing 0 to some other value, like this:

60

Chapter 4. Test Driven Development

Listing 4.20. Breaking the code on purpose

public int compareTo(FootballTeam otherTeam) { if (gamesWon > otherTeam.getGamesWon()) {

return 1;

}

else if (gamesWon < otherTeam.getGamesWon()) { return -1;

}

return 18723;

}

Now rerun the test, and see it fails Good. Revert the change of compareTo() method (so it returns 0 again). Rerun the test to see it pass. Good. This means that your test really verifies this behaviour. And that is exactly what we wanted to know.

I know this advice might sound extreme, but this is the only way to make sure you do not make any silly mistakes at all when writing tests.

61

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]