It's important to be able to do some integration testing without necessarily deploying to an application server or connecting to other corporate infrastructure. This will allow you to test things like:

  • Correct connection of Spring IoC container contexts.

  • Access data using JDBC or ORM tool. This may include, but is not limited to, the correctness of SQL statements, Hibernate queries, JPA entity mappings, and so on.

Spring Framework provides first-class support for integration testing in the spring-test module. The name of the actual JAR file may include the release version, and may also be in the long form org.springframework.test, depending on where you are getting it from (see the section on dependency management for clarification) . This library includes the org.springframework.test package, which contains useful classes for integration testing using the Spring container. This testing is independent of the application server or other deployment environment. These tests run slower than unit tests, but are much faster than equivalent Selenium tests or remote tests that rely on deployment to the application server.

Support for unit and integration testing is provided in the form of an annotation-oriented Spring TestContext Framework. The TestContext framework is independent of the actual testing infrastructure in use, which allows you to instrument tests in various environments, including JUnit, TestNG and others.

Integration testing goals

Support for integration testing in Spring has the following main goals:

  • Manage Spring IoC container caching between tests.

  • Ensuring dependency injection for testbench instances (environment).

  • Provide compliance with integration testing.

  • Provides Spring-specific core classes that help developers write integration tests.

The next few sections describe each goal and provide links to detailed implementation and configuration explanations.

Context management and caching

The Spring TestContext Framework provides sequential loading of ApplicationContext and WebApplicationContext instances from Spring, as well as caching of these contexts. Support for caching loaded contexts is important because initial startup time can be an issue—not because of latency in Spring itself, but because the objects instantiated by the Spring container take time to create. For example, in a project with 50-100 display files, Hibernate may take 10 to 20 seconds to load the display files, and this overhead before running each test in each testbench results in a slower overall testing process, which reduces developer productivity.

Test classes typically declare either an array of resource locations for XML or Groovy configuration metadata—often on the classpath—or an array of component classes that are used to configure the application. These locations or classes are the same or similar to those specified in web.xml or other configuration files for production deployment.

By default, after loading, the configured ApplicationContext is reused for each test. This way, setup resources are spent only once for each test suite, and subsequent test executions are much faster. In this context, the term "test suite" means all tests running in a single JVM - for example, all tests running from an Ant, Maven, or Gradle build for a given project or module. In the unlikely event that a test corrupts the application context and requires a reload (for example, when a bean definition or state of an application object changes), the TestContext framework can be configured to reload the configuration and restore the application context before running the next test.

Dependency injection for test benches

When the TestContext framework loads the application context, it can optionally configure instances of your test classes using dependency injection. This allows you to provide a convenient mechanism for setting up test benches using pre-configured beans from your application context. The big benefit here is the ability to reuse application contexts across different testing scenarios (e.g., to set up Spring-managed object graphs, transactional proxies, DataSource instances, etc.), eliminating the need to duplicate complex testbench setups for individual test cases.

As an example, consider a scenario in which we have a class (HibernateTitleRepository) that implements data access logic for a domain entity Title. We want to write integration tests that test the following areas:

  • Spring Configuration: Mainly, is everything required related to the HibernateTitleRepository bean configuration present correctly and present?

  • Hibernate mapping file configuration: Is everything mapped correctly and have the correct lazy load settings set?

  • Repository logic HibernateTitleRepository: Does the configured instance of this class perform the expected functionality?

Transaction management

One common problem with tests that access a real database is their impact on the state of the persistence store. Even if you use the database for development, state changes can affect future tests. In addition, many operations—such as inserting or modifying persistent data—cannot be performed (or verified) outside of a transaction.

The TestContext framework solves this problem. By default, the framework creates and rolls back a transaction for each test. It is possible to write code that can assume the existence of a transaction. If you call proxy objects transactionally in your tests, they behave correctly according to the configured transaction semantics. Additionally, if a test method deletes the contents of the selected tables while a test-managed transaction is running, the transaction is rolled back by default and the database is returned to the state before the test was executed. Transaction support is provided to a test using the PlatformTransactionManager bean defined in the test application context.

If you need the transaction to be committed (non-standard, but sometimes useful if you want a particular test to populate or modify the database), you can tell the TestContext framework to cause the transaction to commit instead of rolling back, using the @Commit annotation.

See managing transactions using the TestContext framework (see link at the end of the lecture).

Helper classes for integration testing

The Spring TestContext Framework provides several abstract helper classes that make writing integration tests easier. These core test classes provide well-defined interceptors that plug into the testing framework, as well as helper instance variables and methods that allow you to access:

  • ApplicationContext – for performing explicit bean lookups or testing the state of the context as a whole.

  • JdbcTemplate – for executing SQL statements for database queries. You can use such queries to validate the state of the database both before and after executing application code associated with the database, and Spring will ensure that such queries are executed within the same transaction as the application code. When used in conjunction with an ORM tool, false positives should be avoided (see link at the end of the lecture).

Alternatively, you may need to create your own custom application-wide superclass with instance variables and methods specific to your project.

See everything about TestContext framework and its interaction with: “Context management/caching”, auxiliary classes for the framework, and also inject dependencies into test benches and manage transactions using.