Spring Boot Unit Testing Example
1. Introduction
This is an in-depth article related to the Spring Boot Unit Testing. Spring Boot framework has features to build applications. Spring Boot has features related to unit testing the application. The framework has utilities and annotations to perform unit testing. Modules for the Test support are spring-boot-test and spring-boot-test-autoconfigure which supports auto-configuration for tests.
2. Spring Boot Unit Testing
2.1 Prerequisites
Java 8 or 9 is required on the linux, windows or mac operating system. Maven 3.6.1 is required for building the spring and hibernate application.
2.2 Download
You can download Java 8 can be downloaded from the Oracle web site . Apache Maven 3.6.1 can be downloaded fromApache site. Spring framework latest releases are available from the spring website.
2.3 Setup
You can set the environment variables for JAVA_HOME and PATH. They can be set as shown below:
Environment Setup for Java
JAVA_HOME=”/jboss/jdk1.8.0_73″ export JAVA_HOME PATH=$JAVA_HOME/bin:$PATH export PATH
The environment variables for maven are set as below:
Environment Setup for Maven
JAVA_HOME=”/jboss/jdk1.8.0_73″ export M2_HOME=/users/bhagvan.kommadi/Desktop/apache-maven-3.6.1 export M2=$M2_HOME/bin export PATH=$M2:$PATH
2.4 Building the application
2.4.1 Spring
You can start building Spring applications using Spring Boot framework. Spring Boot has minimal configuration of Spring. Spring Boot has features related to security, tracing, application health management and runtime support for webservers. Spring configuration is done through maven pom.xml. The xml configuration is shown as below:
Spring Configuration
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>spring-helloworld</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <properties> <java.version>1.8</java.version> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
You can create a HelloWorldController
class as the web controller. The class is annotated using @RestController
. Rest Controller is used to handle requests in Spring Model View Controller framework. Annotation @RequestMapping
is used to annotate the index()
method. The code for the HelloWorldController
class is shown below:
HelloWorld Controller
package helloworld; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; @RestController public class HelloWorldController { @RequestMapping("/") public String index() { return "Hello World\n"; } }
HelloWorldApp
is created as the Spring Boot web application. When the application starts, beans, and settings are wired up dynamically. They are applied to the application context. The code for HelloWorldApp
class is shown below:
HelloWorld App
package helloworld; import java.util.Arrays; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class HelloWorldApp { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(HelloWorldApp.class, args); System.out.println("Inspecting the beans"); String[] beans = ctx.getBeanDefinitionNames(); Arrays.sort(beans); for (String name : beans) { System.out.println("Bean Name" +name); } } }
Maven is used for building the application. The command below builds the application.
Maven Build Command
mvn package
The output of the executed command is shown below.
The jar file spring-helloworld-0.1.0.jar is created. The following command is used for executing the jar file.
Run Command
java -jar target/spring-helloworld-0.1.0.jar
The output of the executed command is shown below.
Curl command is invoked on the command line for the execution of index
method. The method returns a String “Hello World” text. @RestController
aggregates the two annotations @Controller
and @ResponseBody
. This results in returning data. The ouput is shown as below.
2.5 Unit Testing
2.5.1 Mocks
In Spring, MockMvc
is used to send HTTP requests to the DispatcherServlet
. The assertions are made based on the result obtained from the servlet. @AutoConfigureMockMvc
annotation is used with @SpringBootTest
to inject a MockMvc
instance. The implementation of the Spring Unit Test is shown as below:
HelloWorldControllerTest
package helloworld; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class HelloWorldControllerTest { @Autowired private MockMvc mvc; @Test public void getMessage() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(equalTo("Hello World\n"))); } }
Maven command is used to run the unit test. The command is as below :
Maven Build Command
mvn package
The output of the executed command is shown below.
2.5.2 No Mocks
Spring configuration of unit tests without Mocks is done through maven pom.xml. The xml configuration is shown as below:
Maven pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <!-- Your own application should inherit from spring-boot-starter-parent --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-examples</artifactId> <version>${revision}</version> </parent> <artifactId>spring-boot-example-test-nomockito</artifactId> <name>Spring Boot Test Sample No Mockito</name> <description>Spring Boot Test Sample No Mockito</description> <properties> <main.basedir>${basedir}/../..</main.basedir> </properties> <dependencies> <!-- Compile --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
ExampleTestNoMockitoApplication
is created as the Spring Boot web application. When the application starts, beans, and settings are wired up dynamically. They are applied to the application context. The code for ExampleTestNoMockitoApplication
class is shown below:
ExampleTestNoMockitoApplication
package example.testnomockito; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ExampleTestNoMockitoApplication { public static void main(String[] args) { SpringApplication.run(ExampleTestNoMockitoApplication.class); } }
The implementation of the Spring Unit Test is shown as below:
Unit Test without Mocks
package example.testnomockito; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) public class ExampleTestNoMockitoApplicationTests { @Autowired private ApplicationContext context; @Test public void contextLoads() throws Exception { assertThat(this.context).isNotNull(); } }
Maven command is used to run the unit test. The command is as below :
Maven Build Command
mvn package
The output of the executed command is shown below.
2.5.3 TestNG
TestNG is a unit testing framework for unit testing Spring Boot applications. This framework is inspired from JUnit and NUnit. TestNG is an open source automated testing framework. Spring configuration of unit tests with TestNG is done through maven pom.xml. The xml configuration is shown as below:
Maven pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <!-- Your own application should inherit from spring-boot-starter-parent --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-examples</artifactId> <version>${revision}</version> </parent> <artifactId>spring-boot-example-testng</artifactId> <name>Spring Boot TestNG Sample</name> <description>Spring Boot TestNG Sample</description> <properties> <main.basedir>${basedir}/../..</main.basedir> </properties> <dependencies> <!-- Compile --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <!-- Test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.8.13</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
ExampleTestNGApplication
is created as the Spring Boot web application. When the application starts, beans, and settings are wired up dynamically. They are applied to the application context. The code for ExampleTestNGApplication
class is shown below:
ExampleTestNGApplication
package example.testng; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class ExampleTestNGApplication { private static Log logger = LogFactory.getLog(ExampleTestNGApplication.class); @Bean protected ServletContextListener listener() { return new ServletContextListener() { @Override public void contextInitialized(ServletContextEvent sce) { logger.info("ServletContext initialized"); } @Override public void contextDestroyed(ServletContextEvent sce) { logger.info("ServletContext destroyed"); } }; } public static void main(String[] args) { SpringApplication.run(ExampleTestNGApplication.class, args); } }
The implementation of the Message Service Class is shown as below:
Message Service
package example.testng.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class MessageService { @Value("${name:Greeting}") private String name; public String getMessage() { return "Sending " + this.name; } }
The implementation of the ExampleController class is shown as below:
ExampleController
package example.testng.web; import example.testng.service.MessageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class ExampleController { @Autowired private MessageService messageService; @GetMapping("/") @ResponseBody public String greeting() { return this.messageService.getMessage(); } }
The implementation of the Spring Unit Test is shown as below:
ExampleTestNGApplicationTests
package example.testng; import org.testng.annotations.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class ExampleTestNGApplicationTests extends AbstractTestNGSpringContextTests { @Autowired private TestRestTemplate restTemplate; @Test public void testHome() { ResponseEntity entity = this.restTemplate.getForEntity("/", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("Sending Greeting"); } }
Maven command is used to run the unit test. The command is as below :
Maven Build Command
mvn package
The output of the executed command is shown below.
2.5.4 Auto Configurations
Auto-configuration is the feature of Spring 2.0 framework. Tests for auto-configuration classes start up an ApplicationContext
with the auto-configuration class. AutoConfigurations
class can be under test or dependent on the test., The sample code which shows the autoconfiguration is attached below:
Auto Configuration
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(MessageServiceAutoConfiguration.class));
2.5.5 Smoke Test
The @SpringBootTest
annotation is used in Spring Boot Framework based Smoke Tests. The main configuration class has the annotation @SpringBootApplication
at the declaration of the class. A sample SmokeTest
implementation is shown as below:
Smoke Test
import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class SmokeTest { @Autowired private ExampleController controller; @Test public void contexLoads() throws Exception { assertThat(controller).isNotNull(); } }
2.5.6 Application Behavior
You can write unit tests which assert the application behavior. The application can start up and listen for a connection. The HTTP request can be sent and the response is asserted. A sample class TestHttpRequest
which implements the unit test for application behavior is shown below.
TestHttpRequest
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class TestHttpRequest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Test public void greetingShouldReturnDefaultMessage() throws Exception { assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/", String.class)).contains("Greetings Message); } }
2.6 Full Stack Integration Test
Integration test is used for testing the communication paths between different parts of the module. This is done to ensure that all modules work rightly. You can use Spring Boot to write a full-stack integration test. Sample Full Stack Integration test is implemented for the application built in section 2.4.
Full Stack Integration Test
package helloworld; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.net.URL; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class HelloWorldControllerIntegrationTest { @LocalServerPort private int port; private URL base; @Autowired private TestRestTemplate template; @Before public void setUp() throws Exception { this.base = new URL("http://localhost:" + port + "/"); } @Test public void getMessage() throws Exception { ResponseEntity response = template.getForEntity(base.toString(), String.class); assertThat(response.getBody(), equalTo("Hello World\n")); } }
Maven command is used to run the unit test. The command is as below :
Maven Build Command
mvn package
The output of the executed command is shown below.
2.6 Auto-Configured Tests
You can use the @WebFluxTest
annotation to test the Spring controllers based on Spring Webflux framework. @MockBean
annotation is used to provide mock implementations. @JdbcTest
annotation is used to test JPA applications. @JdbcTest
annotation configures the data source as an in-memory embedded database. @JooqTest
annotation configures a DSLContext. @DataMongoTest
annotation configures an in-memory embedded MongoDB database. This annotation configures a MongoTemplate
. MongoTemplate
scans for @Document
classes. The template configures Spring Data MongoDB database repositories.@DataRedisTest
annotation is used to test Redis applications. The annotation scans for the classes which use @RedisHash annotation. @DataRedisTest
annotation configures the Spring Data Redis database repositories.
@DataLdapTest
annotation configures an in-memory embedded LDAP. This annotation configures a LdapTemplate
. It also scans for @Entry
classes and configures Spring Data LDAP repositories. You can use the @RestClientTest
annotation to test the REST clients. @RestClientTest
annotation auto-configures the dependencies like Jackson, GSON, and Jsonb support. This annotation configures a RestTemplateBuilder
. It also adds support for the MockRestServiceServer
.
2.7 Best Practices
You can isolate the functionality to be unit tested. This is done by limiting the context of loaded frameworks and components. The slices of functionality are loaded when testing spring boot applications. The other best practices are available at the spring boot testing site.
2.8 Error Handling
Spring boot framework has features to handle exceptions and errors. Errors in REST APIs help in presenting the issues to the clients. You can use @ResponseStatus
annotation to specify the Response Status for a specific exception.
2.9 Logging
Spring Boot Framework uses Commons Logging for application logging. Different configurations for logging are provided in the framework. They are Java Util Logging, Log4J2, and Logback. Loggers are pre-configured for printing the output on the console or in the configured file.
3. Download the Source Code
You can download the full source code of this example here: Spring Boot Unit Testing Example