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

Chapter 3. Unit Tests with no Collaborators

Listing 3.12. Money class constructor expected exceptions test

@RunWith(JUnitParamsRunner.class) public class MoneyIAETest {

private final static int VALID_AMOUNT = 5;

private final static String VALID_CURRENCY = "USD";

private static final Object[] getInvalidAmount() { return new Integer[][]{{-12387}, {-5}, {-1}};

}

@Test(expected = IllegalArgumentException.class) @Parameters(method = "getInvalidAmount")

public void constructorShouldThrowIAEForInvalidAmount( int invalidAmount) {

new Money(invalidAmount, VALID_CURRENCY);

}

private static final Object[] getInvalidCurrency() { return new String[][]{{null}, {""}};

}

@Test(expected = IllegalArgumentException.class) @Parameters(method = "getInvalidCurrency")

public void constructorShouldThrowIAEForInvalidCurrency( String invalidCurrency) {

new Money(VALID_AMOUNT, invalidCurrency);

}

}

both tests will succeed only if a given exception is thrown, both test methods use data-providing methods,

there are no explicit assertions in this test.

What I would like to stress here is the importance of giving meaningful names to methods and variables8. They make the test readable and leave no room for doubts as to the role played by each method or variable. Let’s take a look at this line:

Money money = new Money(invalidAmount, VALID_CURRENCY);

By using meaningful names for variables, we have achieved a highly readable test. Just read it: "this line of code creates a new object of the class Money, using an invalid amount and a valid currency". All perfectly clear.

More information on expected exceptions testing can be found in Section 6.4.

3.8. Test Fixture Setting

The term "test fixture" refers to the notion of a "well known and fixed environment in which tests are run so that results are repeatable"9. The steps required to set up such an environment differ, depending on the types of test and tool used, but the basic idea is always the same.

8This issue is further discussed in Section 9.2 and Section 11.6.1 9Citation from Wikipedia, http://en.wikipedia.org/wiki/Test_fixture.

29

Chapter 3. Unit Tests with no Collaborators

It is time to make sure that all elements are in place prior to an SUT’s methods’ being executed. In this section we will learn the basics of JUnit support for test fixture creation. We will learn some new annotations, and discuss the optimal test code structure.

3.8.1. Test Fixture Examples

The automation of the environment set up process is often the most challenging part of testing. This is especially true for integration and end-to-end tests. In the case of unit tests things are usually much simpler, but there are still some issues which should be taken care of. The following table gives examples of test fixtures for different types of test.

Table 3.2. Test fixture examples

type of test

test fixture example

unit test

• creation of new objects (SUT and test doubles),

 

• preparation of input data,

 

 

integration

• resetting the database to the initial state (e.g. so it contains one user with

test

required privileges whose account can be used to perform tests),

 

• copying of files that will be used during tests,

 

 

end-to-end

• installation of a virtual machine that provides the runtime environment for the

test

application,

installation (or cleaning to some initial state) of the web server and database used by the application.

3.8.2.Test Fixture in Every Test Method

Let us introduce now two simple Client and Address classes. Suppose we want to verify that the objects of the Client class are able to store a collection of addresses. Our first approach to the testing of this class might be the following:

30

Chapter 3. Unit Tests with no Collaborators

Listing 3.13. Client object created in each test method

public class ClientTest {

private Address addressA = new Address("street A"); private Address addressB = new Address("street B");

@Test

public void afterCreationShouldHaveNoAddress() { Client client = new Client();

assertEquals(0, client.getAddresses().size());

}

@Test

public void shouldAllowToAddAddress() { Client client = new Client();

client.addAddress(addressA);

assertEquals(1, client.getAddresses().size()); assertTrue(client.getAddresses().contains(addressA));

}

@Test

public void shouldAllowToAddManyAddresses() { Client client = new Client();

client.addAddress(addressA);

client.addAddress(addressB);

assertEquals(2, client.getAddresses().size()); assertTrue(client.getAddresses().contains(addressA)); assertTrue(client.getAddresses().contains(addressB));

}

}

This test is fine, yet it certainly has a lot of repeated code related to the creation of objects. The client variable is created in each test method. If only we could have it being created in advance of each test method’s being executed… Well, it comes as no surprise that such a common concern is addressed by JUnit.

3.8.3. JUnit Execution Model

To avoid repetition in the test code we may exploit the fact that JUnit creates a new instance of a test class before executing any test method marked with the @Test annotation. This means that every instance variable (like addressA and addressB on listing Listing 3.13) are created anew before

execution of any test method: afterCreationShouldHaveNoaddress(), shouldAllowToAddAddress() and shouldAllowToAddManyAddresses().

This means that if a new Client is what we need in every test method, all we have to do is to make it an instance field instead of method variable.

31

Chapter 3. Unit Tests with no Collaborators

Listing 3.14. Client object as an instance field

public class ClientTest {

private Address addressA = new Address("street A"); private Address addressB = new Address("street B"); private Client client = new Client();

@Test

public void afterCreationShouldHaveNoAddress() { assertEquals(0, client.getAddresses().size());

}

@Test

public void shouldAllowToAddAddress() { client.addAddress(addressA);

assertEquals(1, client.getAddresses().size()); assertTrue(client.getAddresses().contains(addressA));

}

@Test

public void shouldAllowToAddManyAddresses() { client.addAddress(addressA); client.addAddress(addressB);

assertEquals(2, client.getAddresses().size()); assertTrue(client.getAddresses().contains(addressA)); assertTrue(client.getAddresses().contains(addressB));

}

}

client is now an instance field.

This test is functionally equivalent to the previous one (Listing 3.13), but a little bit shorter. As can be seen, the creation of the client variable is no longer dispersed across multiple methods, but happens only once. Also, there is no repetition. Had more test methods been available, the advantages of this approach would have been even more pronounced.

This test is also probably a little bit harder to read, because we are obliged to look outside a test method to understand how it works. This is something worth remembering.

3.8.4. Annotations for Test Fixture Creation

In some cases the method presented in the previous paragraphs does not work. In particular, this happens when we need to call some methods on the object of the Client class to fully prepare it for testing.

Another way to solve the same problem is to introduce a setUp() method10 that will be responsible for the creation of all objects. By annotating it with the @Before annotation, we tell JUnit to do exactly that: to execute this method before each test method is executed.

10It is named setUp() for historical reasons (JUnit 3.x had a special method, called setUp(), responsible for setting up the test fixture). As stated before, JUnit 4 does not care about the name: what it responds to are method annotations.

32

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