This chapter covers the test module of Spring and the APIs used for unit and integration testing Spring applications. The following topics are covered here:
TestContext
framework and SpringJUnit4ClassRunner
MockEnvironment
and MockPropertySource
SimpleNamingContextBuilder
and ExpectedLookupTemplate
ReflectionTestUtils
@ContextConfiguration
, ApplicationContextInitializer
, @WebAppConfiguration
, @ContextHierarchy
, @ActiveProfiles
, @ProfileValueSourceConfiguration
, @TestPropertySource
, @DirtiesContext
, @TestExecutionListeners
, @IfProfileValue
, @Timed
, and @Repeat
MockHttpServletRequest
, MockHttpSession
, and ModelAndViewAssert
, as well as Spring beans with request scope and Spring beans with session scopeMockMvc
@Transactional
, @TransactionConfiguration
, @Rollback
, @BeforeTransaction
, and @AfterTransaction
Spring's
TestContext
framework is a generic, annotation-driven framework for unit and integration testing. The framework's resources are located in the org.springframework.test.context
package. This framework believes in the design paradigm "convention over configuration," which means that the framework provides reasonable defaults for every configuration; the user can still override the unconventional aspects through annotation-based configuration. The TestContext
framework provides support for JUnit and TestNG, such as a custom JUnit runner that allows non-invasive POJO test classes.
The framework consists of two classes and three interfaces. The following are the classes:
TestContext
: This class provides the context in which a test is executed. It also makes the context management and caching supports available for the test instance. To load the application context, the ContextLoader
interface (or SmartContextLoader
) is used.TestContextManager
: This class is the main entry point to the TestContext
framework; it manages a single TestContext
class and publishes events to all registered TestExecutionListener
implementations at test execution points. These are the test execution points:The following are the interfaces:
TestExecutionListener
: The TestContextManager
class publishes events to all the registered listeners. This interface defines the listener API to react to the published events.ContextLoader
: This interface loads ApplicationContext
for the Spring integration tests.SmartContextLoader
: This interface is the extension of the ContextLoader
interface and has been introduced in Spring 3.1. A SmartContextLoader
interface processes resource locations, annotated classes, or context initializers. Also, it can set active bean profiles (@ActiveProfiles
) and property sources in the context that it loads.For each test, a TestContextManager
class is being created. The TestContextManager
class handles a TestContext
class for the current test and updates the state of the TestContext
class as the test progresses. For dependency injection, dirty checks, transactional support, and so on, the TestContextManager
class delegates control to the TestExecutionListener
implementations, which in turn implements the actual test execution by providing dependency injection, managing transactions, and so on.
The default TestExecutionListener
implementations are registered in the following order:
ServletTestExecutionListener
: This listener provides the Servlet API mocks for WebApplicationContext
DependencyInjectionTestExecutionListener
: As the name suggests, this listener provides dependency injections for the testDirtiesContextTestExecutionListener
: This listener checks the context—whether any bean is dirtied or not during a test execution; it also handles the @DirtiesContext
annotationTransactionalTestExecutionListener
: This provides transactional supportSqlScriptsTestExecutionListener
: This executes SQL scripts configured via the @Sql
annotationThe TestExecutionListener
implementations externalize the reusable code to instrument tests. When we execute a TestExecutionListener
implementation, we can reuse it across test class hierarchies and projects. Custom TestExecutionListener
implementations can be registered for a test class and its subclasses via the @TestExecutionListeners
annotation. If a custom TestExecutionListener
implementation is registered via @TestExecutionListeners
, the default listeners will not be registered. As a result, the developer has to manually declare all the default listeners in addition to any custom listeners. The following example demonstrates this style of configuration. Usually, we don't need a custom TestExecutionListener
implementation unless we want to perform some custom logic before, during, or after the test method or test class execution. In the following section, we'll create a custom listener to print the test class and method names just before and after test execution.
The following are the steps to create a custom TestExecutionListener
implementation:
SpringTests
.SysOutTestExecutionListener
Java class in the com.packt.listener
package and implement the TestExecutionListener
interface. All implemented methods print information about the test class or the test method. The TestExecutionListener
listener can be reused with any Spring test class. The following is the implementation:public class SysOutTestExecutionListener implements TestExecutionListener { @Override public void afterTestClass(TestContext testContext) throws Exception { ApplicationContext ctx = testContext.getApplicationContext(); System.out.println("In afterTestClass for class = "+testContext.getTestClass()); }
Note that you can get the application context, ctx
, from the TestContext
class to work with the Spring beans. Although I'm not doing any alterations to any bean configuration, you can do so from all the methods in a TestExecutionListner
class, as shown here:
@Override public void afterTestMethod(TestContext testContext) throws Exception { System.out.println("In afterTestMethod for = "+testContext.getTestMethod().getName()); } @Override public void beforeTestClass(TestContext testContext) throws Exception { System.out.println("In beforeTestClass for class = "+testContext.getTestClass()); } @Override public void beforeTestMethod(TestContext testContext) throws Exception { System.out.println("In beforeTestMethod for = "+testContext.getTestMethod().getName()); } @Override public void prepareTestInstance(TestContext testContext) throws Exception { System.out.println("In prepareTestInstance for= "+testContext.getTestInstance()); } }
The SysOutTestExecutionListener
class implements five methods, namely, afterTestClass
, beforeTestClass
, afterTestMethod
, beforeTestMethod
, and prepareTestInstance
. Each method accepts a TestContext
object. A TextContext
object can provide the test method, test class, test instance, application context, and the beans configured in the application context, and so on. We'll check the method execution sequence later.
applicationContext.xml
file directly under the com.packt.listener
package. You don't need to define any bean here. The following is the XML file:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring- beans.xsd"> </beans>
SysOutTestExecutionListener
. The class details are as follows:package com.packt.listener; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:com/packt/listener/applicationContext.xml") @TestExecutionListeners({ SysOutTestExecutionListener.class }) public class TestExecutionListenerTest { @Test public void someTest() throws Exception { System.out.println("executing someTest"); } @Test public void someOtherTest() throws Exception { System.out.println("executing someOtherTest"); } }
The class is annotated with @RunWith
, @ContextConfiguration
, and @ TestExecutionListeners
. By annotating test classes with @RunWith(SpringJUnit4ClassRunner.class)
, we enable the class to get the benefits of Spring unit and integration tests, such as TestContext
, the applicationContext
loading, DI, transaction support, and so on.
The @ContextConfiguration
annotation loads the application context resource from the specified locations
or the @Configuration
annotated classes. In locations
, we pass the XML configuration or the applicationContext
XML location that can be loaded from the classpath.
The @TestExecutionListeners
annotation defines class-level metadata to configure which TestExecutionListener
implementations should be registered with TestContextManager
.
TestExecutionListenerTest
class has two tests. When we execute the test class, the following output is displayed:In beforeTestClass for class = class com.packt.listener.TestExecutionListenerTest In prepareTestInstance for= com.packt.listener.TestExecutionListenerTest@548c491e In beforeTestMethod for = someOtherTest executing someOtherTest In afterTestMethod for = someOtherTest In prepareTestInstance for= com.packt.listener.TestExecutionListenerTest@5cd99967 In beforeTestMethod for = someTest executing someTest In afterTestMethod for = someTest In afterTestClass for class = class com.packt.listener.TestExecutionListenerTest
The beforeTestClass
method is invoked first, and it is invoked only once for the test class; we can access the application context and beans using this method. The prepareTestMethod
is invoked before any test method execution. We can get the test instance and prepare beans or initialize test-specific data from this method. The beforeTestMethod
is executed after prepareTestMethod
but before any test method execution, and then a test is executed. The afterTestMethod
is executed after any test method execution. The afterTestClass
method acts like the destructors in C++, and is invoked only once per class at the end of the last test method's afterTestMethod
call.
You might wonder what the difference is between JUnit 4's @before
and @after
and the TestExecutionListener
methods. The answer is you can access TestContext
in the TestExecutionListener
methods but not in JUnit annotated methods, and TestExecutionListener
logic can be shared with many tests but JUnit annotations are test class specific. For example, our SysOutTestExecutionListener
logic can be shared with any test class; but if we annotate a test method with a JUnit 4 annotation, then that method cannot be shared with all the test classes unless they extend the class.
TestExecutionListener
class is registered via @TestExecutionListeners
, the default listeners will not be registered. This forces the developer to manually declare all default listeners in addition to any custom listeners. The following listing demonstrates this style of configuration:@ContextConfiguration @TestExecutionListeners({ SysOutTestExecutionListener.class, ServletTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class
}) public class TestExecutionListenerTest { }
mergeMode
attribute of @TestExecutionListeners
can be set to MergeMode.MERGE_WITH_DEFAULTS
. The MERGE_WITH_DEFAULTS
part indicates that locally declared listeners should be merged with the default listeners, as shown in the following listing:@ContextConfiguration
@TestExecutionListeners(
listeners = SysOutTestExecutionListener.class,
mergeMode = MERGE_WITH_DEFAULTS
) public class TestExecutionListenerTest { }
The TextContext
framework does not force you to extend any particular class or to implement a specific interface in order to configure the application context. Instead, configuration is achieved simply by declaring the @ContextConfiguration
annotation at the class level.
3.144.89.2