Boot

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.

Spring Boot Unit Testing - Maven Build
Maven Build

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.

Spring Boot Unit Testing - Spring Beans
Spring Beans

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.

Spring Boot Unit Testing - Curl Command
Curl Command

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.

Spring Boot Unit Testing
Spring Unit Test

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.

Spring Boot Unit Testing - No Mockito Maven Execution
No Mockito Maven Execution

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.

Spring Boot Unit Testing - TestNG Maven
TestNG Maven Execution

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.

Spring Boot Unit Testing - FullStack Integration
FullStack Integration Test

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

Download
You can download the full source code of this example here: Spring Boot Unit Testing Example

Bhagvan Kommadi

Bhagvan Kommadi is the Founder of Architect Corner & has around 20 years’ experience in the industry, ranging from large scale enterprise development to helping incubate software product start-ups. He has done Masters in Industrial Systems Engineering at Georgia Institute of Technology (1997) and Bachelors in Aerospace Engineering from Indian Institute of Technology, Madras (1993). He is member of IFX forum,Oracle JCP and participant in Java Community Process. He founded Quantica Computacao, the first quantum computing startup in India. Markets and Markets have positioned Quantica Computacao in ‘Emerging Companies’ section of Quantum Computing quadrants. Bhagvan has engineered and developed simulators and tools in the area of quantum technology using IBM Q, Microsoft Q# and Google QScript. He has reviewed the Manning book titled : "Machine Learning with TensorFlow”. He is also the author of Packt Publishing book - "Hands-On Data Structures and Algorithms with Go".He is member of IFX forum,Oracle JCP and participant in Java Community Process. He is member of the MIT Technology Review Global Panel.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button