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

Chapter 5. Mocks, Stubs, Test Spies

In contrast to real objects of the Phone class, test doubles have no idea about how to behave, so we need to instruct them. This is required for mobile phones (which should return true). For stationary

phones, there is no need to specify a returned value, as mocks created by Mockito return false by default11.

The code in Listing 5.40 does not differ much from the previously shown Listing 5.39. Constructor calls, which defined how objects of the Phone class should behave, were replaced with calls to Mockito’s

mock() and when() methods.

No Winner So Far

So far, so good. Both approaches seems fine. Using real classes in test code seems to be justified by the close relationship between Client and Phone. Both test classes are concise and free of any logic. Good.

5.5.3. A More Complicated Example

But let us stir things up a little bit, where this very solid construction is concerned, by introducing a small change to the Phone class: let us make it behave more intelligently. Phone constructor can recognize if a number belongs to a mobile phone, using pattern matching. Because of this change, there is no need for the constructor’s second boolean parameter, as shown in Listing 5.41.

Listing 5.41. Phone class constructor enhanced

public Phone(String number) { this.number = number;

this.mobile = number.startsWith("+") && number.endsWith("9");

}

Do not do this at home! This is surely not a valid way to recognize mobile phone numbers!

After this change has been introduced, the test, which does not use mocks, needs to be updated. This time, in order to create appropriate phones (mobile and stationary), a knowledge of the internals of the Phone class is required. Without it there is no way a developer could construct a phone of the desired type. The test starts to look as shown in Listing 5.42.

11See Section 5.1.1 for information concerning the default behaviour of Mockito’s test doubles.

92

Chapter 5. Mocks, Stubs, Test Spies

Listing 5.42. No test doubles used - enhanced Phone class version

public class ClientTest {

private final static Phone MOBILE_PHONE = new Phone("+123456789"); private final static Phone STATIONARY_PHONE = new Phone("123123123");

private Client client = new Client();

@Test

public void shouldReturnTrueIfClientHasMobile() { client.addPhone(MOBILE_PHONE); client.addPhone(STATIONARY_PHONE);

assertTrue(client.hasMobile());

}

@Test

public void shouldReturnFalseIfClientHasNoMobile() { client.addPhone(STATIONARY_PHONE);

assertFalse(client.hasMobile());

}

}

The chosen phone numbers must follow the logic of the Phone's constructor.

This version of the ClientTest class is coupled with the implementation of the Phone class. If the patternmatching mechanism used in the Phone constructor changes, the test code will also need to change. The SRP principle has clearly been breached, because this test class is also coupled with the Client class implementation (so ClientTest has more than one reason to change). This is a warning signal. The violation of SRP entails that the DRY principle has also been breached. Surely, there must exist a PhoneTest class that will make sure that the Phone constructor and isMobile() method works as expected! To test this functionality, PhoneTest needs to create identical (or almost identical) instances of phones, as shown in Listing 5.42. If so, then a change in the Phone class will entail changes in two tests - PhoneTest and ClientTest. This is bad.

Surprisingly (or, rather… perhaps not!), the test class based on test doubles remained unchanged. The stubs did not even notice the change of algorithm within the Phone class or the difference in the values of the arguments passed to the constructor, because they do not make use of either of these. They were ordered to return certain values at certain points of execution, and they are still following those orders.

5.5.4. Use Test Doubles or Not? - Conclusion

This issue is worth discussing only in the case of "real" objects: that is, objects that have some business logic and offer some complex behaviour. In the case of DTOs12, or Value Objects13, using a test double will be overkill. Similarly, creating a test double of a java.util.ArrayList is not recommended.

As has been confirmed in some of the preceding paragraphs, testing without test doubles is possible, but carries some serious consequences. First of all, it may result in cluttering up your test code with logic that belongs to collaborators. This results in tests failing unexpectedly, if and when the code of the collaborators changes. Secondly, it makes you repeat the same code constructs (e.g. the creation of the DOCs) across multiple tests. This brings it about that making just a single change to some class or other

12Simple containers for data with getters and setters only. See http://en.wikipedia.org/wiki/Data_transfer_object 13Small simple objects like dates, money, strings. See http://c2.com/cgi/wiki?ValueObject

93

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