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

Chapter 9. Organization Of Tests

for calculations was missing. At some point our client introduced a new monitoring tool, which was consuming data returned by the DataProvider class in order to draw some charts. It transpired that this new tool did not like null values at all, so a change was requested – so that it would return zeros instead of null values.

I took a look at the existing code, and found no tests for the class. It was not possible to test the class thoroughly (because it would have taken too much time), so I decided to only introduce tests which would cover the functionality in question. Because I did not intend to test the whole class, I decided to abandon the ClassNameTest pattern. Instead, I created a new test class and called

it DataProviderMeanValuesZerosInsteadOfNullsTest. Even though it went against the norms of my

working practice, I felt it was the right thing to do. The test class name indicated clearly that it was meant to test the DataProvider class, the suffix informed people that it was a test class, and the rest served to indicate what part of the class functionality was being tested.

If it ever should come about that we decide to cover the whole class with tests (not very

likely, though…), then the DataProviderMeanValuesZerosInsteadOfNullsTest will probably

be merged into it.

9.2.2. Test Method Names

JUnit, like all current testing frameworks, give you complete freedom when it comes to naming test methods. This is because identification of test methods is based on annotations and not on method names. With freedom, though, comes a need to make choices, and many different options to choose from. Let’s discuss this issue and see if we can get to choose the best one.

Please refer to Section 10.1 for additional discussion about why some naming schemas are more appropriate than others.

Historically, method names were prefixed with the word test - e.g. testConstructor(), testReturnsNonNullArray(), etc. This naming schema was popularized (or rather enforced) by older versions of JUnit, which treated all methods that followed this pattern as test methods and executed them during tests execution (ignoring any methods with different names). And for a long time everyone was happy with it.

However, the limitations of this naming schema started to show up. First of all, it was not obvious what a particular test method was all about, just by looking at its name. It was quite common to find lengthy test methods which, under a common name such as, for example, testUserAdd(), verified many features of a class being tested. This was especially painful when dealing with failed tests, as there was not much one could learn from the information that "testUserAdd() has failed". Such an error message does not inform us about exactly what has failed: i.e. which feature of the class tested is not working properly. Because of this, method names started to grow and contain much more information. For example testConstructor() was split into several more focused test methods with names like

testConstructorThrowsExceptionForInvalidArguments(), for example.

This was definitely a step in the right direction, but still not quite good enough. Developers at this time were fascinated by the expressiveness of new dynamic languages (e.g. Ruby2) and fluent interfaces3, and

2http://ruby-lang.org

3http://en.wikipedia.org/wiki/Fluent_interface

193

Chapter 9. Organization Of Tests

this meant they were really irritated by the presence of this obligatory test prefix. Many of us demanded that failed test messages should be readable as proper English sentences (or, at least, be as close to this ideal as possible). The rescue came with a new wave of testing frameworks (TestNG in particular) which used annotations instead of method names patterns to recognize test methods. This made it possible to come up with a more readable naming pattern.

This new pattern was based on the idea that the test method name should convey information about the following:

preconditions of the test - the state of the SUT and the environment before the test,

triggering actions - what makes the SUT act in the expected way,

expected results of the test - what should be returned by the SUT, or what state the SUT should be in after the test is finished, or what actions should be performed by the SUT during the test,

optionally, the name or role of the SUT,

in addition, the test prefix was replaced with should, which allows one to create specification-like method names.

Of course, it is usually not possible to include a full description of all these kinds of information in the method name itself. What is possible, though, is to include just enough details to convey the main purpose of the test. Also, each of the elements can be omitted, if its presence does not enhance the readability of the test method. Table 9.1 provides some examples of test method names which follow the guidelines described above.

Table 9.1. Examples of test method names

class name

test methods

 

 

 

constructorShouldThrowExceptionForNullUser()

OrderTest

 

constructorShouldCreateValidOrderForValidUser()

 

 

 

 

shouldNotAcceptDictionaryBasedPasswords()

 

 

 

shouldNotAcceptPasswordWithoutDigits()

PasswordValidatorTest

 

shouldNotAcceptShortPasswords()

 

 

 

 

shouldAcceptLongComplicatedPassword()

 

 

 

shouldAllowToBookTableForOneHourAndMore()

 

 

 

shouldDisallowToBookForLessThanHour()

BookingSystem

 

shouldAllowToCreateRecurrentReservation()

 

 

 

 

shouldAllowToCancelExistingReservation()

 

 

In the case of the examples presented in Table 9.1, all test method names specify exactly what is expected

of the SUT (e.g. shouldThrow…(), shouldNotAccept…(), shouldAllow…()), and under what conditions or with what arguments (e.g. …ForNullUser(), …PasswordsWithoutDigits(), …ExistingReservation()).

When a test fails, the first line of the message already furnishes us with enough information to understand what it is that is not working properly.

194

Chapter 9. Organization Of Tests

What is attractive about this naming schema is that it leaves some room for customization. Some of the examples shown in Table 9.1 do not start with should, but with the constructor prefix, which makes it clearer which part of the SUT is being tested by a particular method.

Test method names are of secondary importance compared to their content, but they can push your thinking in the right direction - or, for that matter, the wrong one. The use of the "should" prefix helps (some people would say forces) us to focus on the expected behaviour of the SUT, and leads to "testing behaviour and not methods" (see Section 10.1). Thinking in terms of the responsibilities of the SUT will make you test at the right level of abstraction. This is especially important when coding code-first, because it is then so very easy to test implementation details instead of the behaviour of an external SUT.

9.2.3. Naming of Test-Double Variables

Let us take a look at the variable names we have used so far when working with test doubles. An example is given in the listing below.

Listing 9.2. Original variables' names

Messenger messenger;

TemplateEngine templateEngine;

MailServer mailServer;

The names of variables, as shown in Listing 9.2, inform us about the types of objects (e.g. it is obvious that mailServer is of the MailServer type). However, they do not convey any information concerning the role which these objects play in our tests. One of them is an SUT, another one a test spy, another a stub. Yet this difference is not expressed by the names of the variables in test code, and can only be discovered on closer inspection.

Some people suggest giving more intention-revealing names to all variables. For example:

Listing 9.3. Intention-revealing names of variables

Messenger sut;

TemplateEngine stubTemplateEngine;

MailServer spyMailServer;

This version of the names of the variables gives much more insight into their purpose in test class. A single glance is sufficient to let us know that the real testing is to be done using sut and spyMailServer, while stubTemplateEngineReq is only assisting in this process.

Another variation of this idea is to reverse the order and put the type of test double as a suffix, e.g. mailServerSpy, templateEngineStub. This way the information concerning the type of the object is more easily visible – something which some people consider more valuable.

It is up to you to decide whether you find this worthwile. With short tests, with a very limited number of DOCs (as they should be), these additional hints, embedded within the variable names, are rather unnecessary. If you have more DOCs and more complex tests than it may help you to understand the tests. On the other hand, short, simple, focused tests are what we should be striving for, so instead of renaming variables, maybe it would be better just to write better tests.

Whatever naming scheme you decide to follow, please make sure you feel comfortable with it (and that so do your colleagues).

195

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