7.2. Integration testing with Spock

In chapter 6, you saw various techniques used to mock collaborator classes so that only the class under test affects the outcome of the tests. In some cases, however, you don’t want to mock collaborators but want to test multiple real classes together.

An integration test spans multiple Java classes (instead of just one) and examines the communication among them. Common scenarios that need integration tests are database mappings, security constraints, communication with external systems, and any other cases where testing is focused on a module rather than a single class.

7.2.1. Testing a Spring application

To start, you’ll look at a standalone application powered by a Swing user interface that manages the warehouse inventory of an e-shop (see figure 7.4).

Figure 7.4. A simple database application with a Swing user interface

The application is based on the Spring framework and saves all its data in a JPA/Hibernate database, which in this case is an HSQLDB[6] file. The design of the application is straightforward. In the middle is the Spring dependency injection container, and all other classes revolve around it, as shown in figure 7.5.

6

See the HyperSQL website at http://hsqldb.org/ for more information on HSQLDB.

Figure 7.5. The Spring context initializes all Java classes.

The Spring context that binds all other classes is an XML file defined in src/main/resources/spring-context.xml. You want to write a Spock test that examines the Hibernate mappings for the Product class. To achieve that, you need to test the whole chain of database loading and saving. The following classes should be tested:

  • The ProductLoader class, which is the DAO
  • The JPA entity manager that manages database mappings
  • The Datasource that provides access to the real database.

The good news is that Spock already contains a Spring extension that instantly recognizes the @ContextConfiguration[7] annotation provided by the Spring test facilities, as shown in the following listing.

7

And the @SpringApplicationConfiguration annotation from Spring Boot.

Listing 7.1. Access Spring context from a Spock test

In this listing, you can see that all testing facilities and annotations are already offered by Spring. Spock automatically understands that this file uses a Spring context and allows you to obtain and use Spring beans (in this case, ProductLoader) as in normal Java code.

The important line here is the @ContextConfiguration annotation because it’s used by Spock to understand that this is a Spring-based integration test. Notice also that you use the Spring @Sql annotation, which allows you to run an SQL file before the test runs. This is already offered by Spring and works as expected in the Spock test.

The resulting test is an integration test, because the real database is initialized, and a product is saved on it and then read back. Nothing is mocked here, so if your database is slow, this test will also run slowly.

Options for Spring testing

The Spring framework contains a gazillion options when it comes to testing. Explaining them all is outside the scope of this book. You should consult the official Spring documentation (https://spring.io/docs). This chapter presents some techniques that prove that Spock and Spring play well together.

A nice facility offered by Spring is the automatic rollback of database changes during a unit test, as shown in the following listing. This is an effective way to keep your unit tests completely independent from one another. Activating this behavior is (unsurprisingly) done by using standard Spring facilities that apply automatically, even in the case of a Spock test.

Listing 7.2. Rolling back database changes automatically

The test code in this listing is exactly the same as in listing 7.1. I have only added two extra Spring annotations. The @Transactional annotation notifies Spring that this test will use database transactions. The @Rollback annotation instructs Spring to revert[8] all database changes performed inside the Spock feature method after the test finishes.

8

The default behavior by Spring is to revert all transactions. I show the @Rollback annotation for emphasis only.

Even if your Spock test deletes or changes data in the database, these changes won’t be persisted at the end of the test suite. Again, this capability is offered by Spring, and Spock is completely oblivious to it.

In summary, Spock support for Spring tests is as easy as marking a test with the Spring test annotations. If you’ve written JUnit tests by using SpringJUnit4ClassRunner, you’ll feel right at home.

7.2.2. Narrowing down the Spring context inside Spock tests

If you’ve written Spring integration tests before, you should have noticed two serious flaws of the Spock tests shown in listings 7.1 and 7.2. Both tests use the same Spring context as the production code. The two flaws are as follows:

  1. Tests use the same database as production code. This isn’t desirable and sometimes not even possible because of security constraints.
  2. The Spring context initializes all Java classes even though not all of them are used in the Spock test.

For example, in the Swing application, the Spock test also creates the Swing class for the GUI even though you never test the GUI. The Spock tests shown in listings 7.1 and 7.2 might not run easily in a headless machine (and build servers are typically headless machines).

The recommended way to solve these issues is to use a different Spring context for the tests. The production context contains all classes of the application, and the test context contains a reduced set of the classes tested. A second XML file is created, as shown in figure 7.6.

Figure 7.6. Creating a second Spring context just for tests

With the reduced context, you’re free to redefine the beans that are active during the Spock test. Two common techniques are replacing the real database with a memory-based one and removing beans that aren’t needed for the test. If you look at the contents of the reduced-text context file, you’ll see that I’ve removed the GUI class and replaced the file-based datasource with an in-memory H2 DB[9] with the following line:

9

You can find more information about the H2 database at www.h2database.com/html/main.html.

<jdbc:embedded-database id="dataSource" type="H2"/>

The in-memory database is much faster than a real database, but it works for only small datasets (you can’t easily use it as a clone of a real database). Because unit tests use specific datasets (small in size) and also need to run fast, an in-memory database is a good candidate for DB testing.

The context for the Spock test is now simplified, as shown in figure 7.7.

Figure 7.7. Spring context for tests uses an in-memory database and no GUI classes.

To run the test, you inform Spring of the alternative context file. Spock automatically picks up the change, as shown in the following listing.

Listing 7.3. Using a reduced Spring context for unit testing

You can find the reduced Spring context at GitHub.[10] Because this test runs with an in-memory database, it’s much faster than the original test shown in listing 7.2. Also you removed the GUI class from the context, so this unit test can run in any Unix/Linux system in a shell environment (the typical case for build servers).

10

You need to examine your own application and decide what you’ll discard/replace in the test context. A good starting point is to remove all beans that aren’t used in your tests.

7.2.3. Directly accessing the database with Groovy SQL

At this point, you’ve seen that a Spock test has access to Spring beans without any special configuration,[11] and that you can use common Spring features as testing aids.

11

JUnit tests need the special SpringJUnit4ClassRunner in order to access Spring beans.

The additional advantage of Spock tests is that you also have all Groovy tools and libraries at your disposal. I introduced you to some essential Groovy facilities back in chapter 2, but you should spend some extra time exploring the full Groovy documentation to see what’s available to help you while writing Spock tests for your application needs.

A handy Groovy feature not mentioned in chapter 2 (because it’s mainly a nice-to-have feature) is the Groovy SQL interface.[12] The Groovy SQL class is a thin abstraction over JDBC that allows you to access a database in a convenient way. You can think of it as a Spring JDBC template on steroids.

12

The takeaway of this section is that you can easily use Groovy libraries to do what you want. Groovy SQL is used as an example. Details of the Groovy SQL interface are provided at http://docs.groovy-lang.org/latest/html/api/groovy/sql/Sql.html.

Let’s assume that you want to verify that the DAO of the e-shop brings back all products in alphabetical order. You can initialize your database by using Groovy SQL, as shown in the next listing.

Listing 7.4. Using Groovy SQL to prepare the DB in a Spock test

The Groovy SQL interface is a powerful feature. It supports all SQL statements you’d expect (schema creations/data writing/data querying), and explaining all its capabilities is beyond the scope of this book. It can be used both in production code and in Spock tests.

I tend to use it when I want to do something strange on the DB (perhaps re-create an error condition) that’s normally not possible via the DAOs of the application. Be careful when using it in your Spock tests, because as you’ve seen in listing 7.4, it gets direct access to the database, so it acts outside the caches of JPA/Hibernate.

Despite these shortcomings, it’s a natural Groovy way to access the DB, and you’ll find its code compact and comfortable. The last example can be further improved by extracting the common SQL statement in its own string, as shown in the next listing.

Listing 7.5. Using Groovy SQL to prepare the DB in a Spock test—improved

A final note regarding Groovy SQL is that if you use it in multiple test methods, it’s best to make it a @Shared field so that it’s created only once. Otherwise, performance of your unit tests will suffer.

7.2.4. Integration testing with other containers (Java EE and Guice)

The example application in the previous paragraph was based on the Spring container because the Spring framework is mature and popular among Java developers. If you’re not using Spring, chances are that your application is based on Java EE. In that case, the respective facilities offered by Spring in integration tests can be replicated by using Arquillian, a test framework for Java EE applications that acts as a testing container and allows access to EJBs, CDI injection,[13] and other enterprise services.

13

The Java spec for dependency injection.

Arquillian (http://arquillian.org/) natively supports JUnit tests, but for Spock tests, you need the Spock-Arquillian extension (https://github.com/arquillian/arquillian-testrunner-spock). The extension has its own repository and a different lifecycle than Spock releases. It works by creating a special runner that brings the Arquillian facilities inside the Spock test.

Apart from Spring, the core Spock distribution also includes support for the Guice dependency injection framework (https://github.com/google/guice). In a similar manner, it allows you to access Guice services/beans inside the Spock test.

If the dependency injection framework you use is something else (other than Spring, Guice, and Java CDI), and there isn’t a Spock extension for that by the time you’re reading this book, you have two choices:

  • Manually initialize and inject your services in the Spock setupSpec() method.[14]

    14

    You can find the code in ManualInjectionSpec in GitHub (the second test method).

  • Find a way to initialize the DI container programmatically inside the Spock test.

The first option isn’t practical because you have to write a lot of boilerplate code that’s usually not needed, going against the mentality of writing Spock tests in the first place (compact and readable tests).

The second way depends on the capabilities of the container you use and whether it supports declarative or programmatic configuration. As an example, assume that the Spock-Spring extension didn’t exist. The Spring container can be still created programmatically, as shown in the next listing.

Listing 7.6. Manual Spring context creation

This Spock test still has access to the Spring context because it creates one manually. Notice the lack of any extra annotations in this listing. If your dependency injection framework supports programmatic initialization, you can still write Spock integration tests without needing a special extension.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.7.240