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

Chapter 1. On Tests and Tools

there? Why couldn’t one smart object deal with the client’s request? Well, the inhabitants of an OO world have very limited knowledge, and only a small set of abilities. Each of them has very constrained functionality or, to put it another way, each of them cannot do much on its own. Thus they are forced to cooperate in order to achieve anything useful (from the user’s point of view). This results in the following way of acting:

I am only a simple web controller, so I cannot fetch the data from the database for you. But I know a guy – call him UserDAO – that might help. So I will pass your request on to him. Ah! I have just remembered that UserDAO does not understand what an HTTP request is. I will extract the information he needs and pass it on to him. Let us wait now for his answer.

— Anonymous Web Controller Anonymous Web Application (circa 2003)

That is how it works. In fact, a lot of classes do nothing more than pass on messages, and maybe also transform them in some manner.

If you think about it, there are not many workers (that is classes that do a real job) out there. At least, not many workers written by you. Object-relational mapping framework (ORM3)? You surely will not have written one. After all, why should you, when there are so many robust solutions around? DependencyInjection container (DI4)? Not likely. Logging framework? No. If you think about the amount of real business logic in your application you might be surprised how little there is. Of course, you do have some business logic. That is why your customer placed an order for a new application. But you probably used a lot of ready-to-be-used elements that do a lot of work for you. And that is fine, because code reuse is a fantastic way to build applications fast, allowing you to concentrate exclusively on the customdesigned elements. But if so, then quite probably many of your classes are only tying things together by passing appropriate messages to appropriate collaborators. They coordinate the work of others. We will call such classes managers. Their work is substantially different from what workers do.

As you will soon see, this difference has a serious impact on testing.

1.2. Types of Developers' Tests

Having the picture of an OO system in mind, we can try to visualize the parts of the system affected by each type of test. This will help us to understand the scope and purpose of each kind of developers’ test.

But before we proceed, let me introduce two important terms that will be used throughout the book: SUT and DOC. Both were popularized by [meszaros2007] and are used frequently when discussing testing issues.

By SUT, or System Under Test, we understand the part of the system being tested. Depending on the type of test, SUT may be of very different granularity – from a single class to a whole application. A DOC, or Depended On Component, is any entity that is required by an SUT to fulfill its duties. Usually a DOC is of the same granularity as the SUT, e.g. if the SUT is a class, then it uses other classes, if it is a module, then it collaborates with other modules.

I will be using the terms "DOCs" and "collaborators" interchangeably.

3See http://en.wikipedia.org/wiki/Object-relational_mapping

4See http://en.wikipedia.org/wiki/Dependency_injection

3

Chapter 1. On Tests and Tools

The following sections will introduce very briefly the various kinds of test. Much more could be said about each of them, but right now let us stick to the picture of the OO system and just try to see which part of it is covered by each kind of test.

1.2.1. Unit Tests

Unit tests focus on single classes. They exist to make sure that your code works. They control all aspects of the context in which the class to be tested is executed, by replacing real collaborators with test doubles5. They know nothing about the users of the system they put to the test, and are unaware of layers, external systems and resources. They run incredibly quickly, and are executed frequently.

This is shown in Figure 1.2, where only one object is clearly visible and all other elements of the system are greyed out. The single visible element is an SUT - the object to be tested. The greyed out elements symbolize those parts of the system not touched fully by the test, or replaced by various test doubles. Horizontal lines represent borders of layers (e.g. view, services, DAO layers). As the picture shows, a unit test is located inside one layer.

Figure 1.2. Scope of a unit test

Not every test run with a unit testing framework is a unit test! Make sure that your unit tests conform to the definition presented in Section 2.1!

1.2.2. Integration Tests

Integration tests focus on the proper integration of different modules of your code, including - and this is especially valuable - with code over which you have no control. An example might be a connection between your business classes and an OSGi container, ORM framework or with a web services framework. Even though the integration tests cover a much wider area of code than unit tests, they still test code as it looks from the developer’s standpoint.

Integration tests run much more slowly than unit tests. They usually require some resources (e.g. an application context) to be set up before they can be executed, and their execution involves calling some entities that tend to respond slowly (e.g. databases, file system or web services). In order to verify the results of integration tests, it is often necessary to look into external resources (e.g. issue an SQL query).

5Test doubles are fake replacements of real parts of the system (e.g. classes or modules). This topic will be discussed in detail in Chapter 5, Mocks, Stubs, Test Spies.

4

Chapter 1. On Tests and Tools

Figure 1.3. Scope of an integration test

As Figure 1.3 shows, integration tests usually extend across a few layers (e.g. when testing whether your services work correctly with a DAO layer). They execute code written by your team, but also code from third-party libraries used by the tested application. As with unit tests, vast areas of the system are either not touched by integration tests or are replaced by test doubles. Integration tests usually do not touch the user interface (the GUI). Because of this, the client (user of the system) is not shown in the picture.

1.2.3. End-to-End Tests

End-to-end tests exist to verify that your code works from the client’s point of view. They put the system as a whole to the test, mimicking the way the user would use it. As such they extend across all layers. Test doubles are rarely used in end-to-end tests – the point is to test the real system. End-to-end tests usually require a significant amount of time to execute themselves.

Figure 1.4. Scope of an end-to-end test

Figure 1.4 shows an end-to-end test that puts to the test elements from all layers - from the front end (GUI, web services layer or any other external API of the tested system) to the storage layers (e.g. database storage). End-to-end tests are initiated through a request identical to those issued by real users of the system (e.g. clicks on GUI elements).

5

Chapter 1. On Tests and Tools

1.2.4. Examples

Table 1.1 gives examples of each type of tests.

Table 1.1. Types of test example

type of test

test examples

 

 

unit test

• An object of the class FootballPlayer should change its status to fired after

 

receiving a second yellow card.

 

• A constructor of the class Product should throw an IllegalArgumentException

 

(with meaningful message) if the price argument is less than 0.

 

 

integration

• An invocation of deleteAccount() method of the class UserService with an

test

argument ID of value 1 should result in removal of the account with this ID

 

from the database.

 

• When asked for an item with ID = 5 for a second time, the ItemDAO class

 

should not touch the real database, but fetch the requested item from the cache

 

instead.

 

ParcelService should communicate with some web service, in order to find

 

the parcel’s details, and send an email with the appropriate error information

 

(using EmailService), if the parcel is not found.

 

 

end-to-end

• A logged on user can add comments to any public picture by clicking on the

test

“add comment” button next to it. Guest users (users not logged on) can see

 

this comment after it is published, but cannot post their own comments.

 

• When a shop owner adds a new product to his shop using an Add Product

 

form, it will be possible to locate this product using a Search Form by entering

 

its name in the search field.

 

• When a user sends his/her geo-location data using a whatCityIsThis web

 

service, the system should respond with a city name.

 

 

Table 1.2 presents examples of SUTs and DOCs for each type of test. It shows how SUTs and DOCs "grow" when moving from unit tests (smaller), via integration tests (medium), to end-to-end tests (large). The difference in granularity is clearly visible. In the case of unit tests, the SUTs and DOCs are simply classes. Integration tests act at the level of modules or layers. In the case of end-to-end tests, it is the whole application that is tested (making the application itself into an SUT), and other applications are collaborators (DOCs).

Table 1.2. Examples of SUT and DOC

type of test

SUT example

DOC example

 

 

 

 

UserService

UserDAO

unit test

 

 

Invoice

Product

 

 

 

 

Client

Account

 

 

 

 

DAO layer (ORM based)

Hibernate

 

 

 

integration test

DAO layer (JDBC based)

MySQL 5

 

 

 

 

FullTextIndexer module

FileStorage module

 

 

 

6

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