The Spring MVC test framework makes unit testing and integration testing of the Spring MVC controller more meaningful by offering first class JUnit support. It helps in testing all the aspects of the controller method that have not been tested before. It allows us to test these aspects in depth without starting a web container.
In order to perform a test on the Spring MVC framework, the Spring TestContext
framework along with JUnit or TestNG makes it so simple by providing an annotation-driven unit and integration testing support. The Spring TestContext
framework can be tested by annotations such as, @RunWith
, @WebAppConfiguration
, and @ContextConfiguration
, to load the Spring configuration and inject the WebApplicationContext
into the MockMvc for the unit and the integration test.
We can configure the Spring TestContext
framework by updating pom.xml
with the required dependencies, such as spring-test
, junit
, and mockito-all
. The following table explains them in detail:
Group ID |
Artifact ID |
Version |
Description |
---|---|---|---|
|
|
3.2.4 release |
It supports unit and integration testing of the Spring components |
|
|
1.9.5 |
The library of the Mockito mocking framework |
|
|
4.10 |
The library of the JUnit framework |
You'll find the following code at pom.xml
:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.packt.Spring.chapter7.springmvc</groupId> <artifactId>SpringMVCPayrollSystem</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>SpringMVCPayrollSystem Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <spring.version>3.2.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <!-- Test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.2.4.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>SpringMVCPayrollSystem</finalName> </build> </project>
The Spring Framework provides the annotations that can be used to perform unit and integration testing with the TestContext
framework. Here, we will discuss the two important annotations: @ContextConfiguration
and @WebAppConfiguration
.
This annotation is used to set ApplicationContext
for the test classes by taking the actual configuration file with the file path. In the following code, we have given the file, so it will take the relative path as the root package. We can also give the exact path by specifying the file: prefix
. Also, we can pass more than one configuration file using a comma separator, as shown here:
@ContextConfiguration ({"classpath*: SpringDispatcher-servlet.xml"}) public class EmployeeControllerTestWithMockMvc { // class body }
The @ContextConfiguration
annotation caches the ApplicationContext
for us and puts it in the static memory for the entire duration of the test or the test suite. And the entire test executes it in the same JVM because ApplicationContext
is stored in the static memory. If the second JVM is there, it will not have access to the static context, and it will result in a second ApplicationContext
being created.
It is a class-level annotation used to create a web version of the application context in the Spring Framework. It is used to denote that the ApplicationContext
, which is loaded for an integration test and used by that class, is an instance of WebApplicationContext
. It is important to note that the @WebAppConfiguration
annotation must be used with the @ContextConfiguration
annotation:
@WebAppConfiguration @ContextConfiguration ({"classpath*: SpringDispatcher-servlet.xml"}) public class EmployeeControllerTestWithMockMvc { // class body }
The MockMvc is a key part of the Spring MVC Test framework, which can be used to write the tests for the applications developed using the Spring MVC. It is the entry point for Spring MVC testing. The MockMvc
mock the entire Spring MVC infrastructure and is created using the implementations of the MockMvcBuilder
interface. In order to use the Spring MVC testing, the first step is to create an instance of MockMvc. There are four static methods in the MockMvcBuilders
class.
They are as follows:
ContextMockMvcBuilder annotationConfigSetup(Class…configClasses)
: Use this method when you need to configure the application context using Java configuration.ContextMockMvcBuilder xmlConfigSetup(String… configLocations)
: Use this method when you need to configure the application context by using the XML configuration files.StandaloneMockMvcBuilder standaloneSetup(Object… controllers)
: You can use this method when you need to configure the test controller manually, and when you want to run the individual components for testing. We don't need to configure the entire application context; instead we only need to configure and execute the associated controller component files.InitializedContextMockMvcBuilder webApplicationContextSetup(WebApplicationContext context)
: This method must be used when you have already fully initialized the WebApplicationContext
object.Here, we have created the MockMvc instance using MockMvcBuilders
and calling the standaloneSetup()
method after passing an instance of the controller class as a parameter and then building it by calling the build()
method, as shown in the following code snippet:
private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.standaloneSetup (employeeController).build(); }
Once we have an instance of MockMvc, we can perform the testing using MockMvc. We can send the HTTP request after specifying all the details, such as the HTTP method, the content type, and so on. And then, we can verify the results.
To perform the assertion, first we use the instance of MockMvc and then we call the perform()
method to pass a relative path to run the test case. And then, we can verify the different components inside the controller using andExpect
. The andExpect(status().isOk())
is used to check for a 200 status. Similarly, we can perform the contentType
validation, the xpath
validation, validate data in the model, URL validation, and the view name validation.
The sample code for this is as shown here:
this.mockMvc .perform(get("/employee")) .andExpect(status().isOk()) .andExpect(view().name("hello")) .andExpect(model().attribute("name", "Hello World!")) .andExpect( model().attribute("greetings", "Welcome to Packt Publishing - Spring MVC !!!"));
This is a JUnit annotation. It executes the tests in a class annotated by the @RunWith
annotation, or extends a class annotated by the @RunWith
annotation by invoking the class passed as a parameter, which means that the tests in the annotated class are not executed by the in-built API in the JUnit framework, the runner class used to execute the test case. In order to use the Spring's JUnit class runner for running the test cases within the Spring's ApplicationContext
environment, passed spring's SpringJUnit4ClassRunner
class as parameter.
So now, we have the complete code to perform the testing of the EmployeeController
controller using the Spring MVC test framework. We will use the MockMvc that will mock the entire Spring MVC infrastructure. We will create a MockMvc instance in the method annotated by the @Before
annotation, so that it will be available before the test starts.
You'll find this code in EmployeeControllerTestWithMockMvc.java
:
package org.packt.Spring.chapter7.springmvc.controller; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.MockitoAnnotations; import org.packt.Spring.chapter7.springmvc.controller.EmployeeController; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration({ "classpath*:SpringDispatcher-servlet.xml" }) public class EmployeeControllerTestWithMockMvc { @InjectMocks private EmployeeController employeeController; private MockMvc mockMvc; @Before public void setup() { MockitoAnnotations.initMocks(this); this.mockMvc = MockMvcBuilders.standaloneSetup(employeeController).build(); } @Test public void testHome() throws Exception { this.mockMvc .perform(get("/employee")) .andExpect(status().isOk()) .andExpect(view().name("hello")) .andExpect(model().attribute("name", "Hello World!")) .andExpect( model().attribute("greetings", "Welcome to Packt Publishing - Spring MVC !!!")); } }
Now, we can run the test case by right-clicking on the test and then choosing Run As | JUnit Test. We can verify in the JUnit view as the test case should run successfully, as shown here: