Spring boot integration test example

Learn to write integration tests for given Spring rest controller apis using @SpringBootTest and Junit 5. This technique can be applied to spring boot as well as spring mvc applications, both.

1. What to test in integration testing

While doing integration testing in spring boot applications, we shall keep in mind that:

  • An integration test is supposed to test whether different modules are bounded correctly and if they work as expected.
  • The integration tests shall not utilize the actual production dependencies (e.g. database/network) and they can mimic the certain behaviors.
  • The application shall run in ApplicationContext and run tests in it.
  • Spring boot provides @SpringBootTest annotation which starts the embedded server, creates a web environment and then enables @Test methods to do integration testing. Use it’s webEnvironment attribute for it.
  • It also creates the ApplicationContext used in our tests.
  • It is good to use h2 in-memory DB for mimicking the database. Though it is not mandatory and we can use mockito to mock the database interactions.
  • It is recommended to use test specific configurations using @TestConfiguration annotation.

TIP: Spring boot provides lots of specialized annotations to test certain part of applications e.g. @WebMvcTest to test web layer or @DataJpaTest to test persistence later.

Use @SpringBootTest for tests that cover the whole Spring Boot application from incoming request to database.

2. Write integration tests with @SpringBootTest and Junit 5

2.1. Maven dependencies

Start by including required dependencies. We need to use spring-boot-starter-test which will internally use spring-test and other dependent libraries.

<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>

	<!-- exclude junit 4 -->

	<exclusions>
		<exclusion>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<!-- Junit 5 -->
<dependency>
	<groupId>org.junit.jupiter</groupId>
	<artifactId>junit-jupiter-api</artifactId>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.junit.jupiter</groupId>
	<artifactId>junit-jupiter-engine</artifactId>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.junit.platform</groupId>
	<artifactId>junit-platform-launcher</artifactId>
	<scope>test</scope>
</dependency>

2.2. REST Controller

Here is the Spring boot rest controller, we will be writing tests for.

  • The controller has dependency on EmployeeRepository class for persistence.
  • getEmployees() method returns list of all employees. Typically, in real applications it will accept pagination parameters.
  • addEmployee() api need access to the request context using ServletUriComponentsBuilder.
  • addEmployee() api returns HTTP status and header using ResponseEntity class.
import java.net.URI;
import java.util.ArrayList;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.howtodoinjava.rest.dao.EmployeeRepository;
import com.howtodoinjava.rest.model.Employee;
import com.howtodoinjava.rest.model.Employees;

@RestController
public class EmployeeController 
{
	@Autowired
    private EmployeeRepository employeeRepository;

	@GetMapping(path="/employees", produces = "application/json")
    public Employees getEmployees() 
    {
		Employees response = new Employees();
		ArrayList<Employee> list = new ArrayList<>();
		employeeRepository.findAll().forEach(e -> list.add(e));
		response.setEmployeeList(list);
        return response;
    }
    
    @PostMapping(path= "/employees", consumes = "application/json", produces = "application/json")
    public ResponseEntity<Object> addEmployee(@RequestBody Employee employee) {       
                
        //add resource
    	employee = employeeRepository.save(employee);
        
        //Create resource location
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                                    .path("/{id}")
                                    .buildAndExpand(employee.getId())
                                    .toUri();
        
        //Send location in response
        return ResponseEntity.created(location).build();
    }
}

2.3. Integration tests

The test class given below contains integration tests for the spring boot rest controller mentioned above. This test class:

  • uses @SpringBootTest annotation which loads the actual application context.
  • uses WebEnvironment.RANDOM_PORT to create run the application at some random server port.
  • @LocalServerPort gets the reference of port where the server has started. It helps in building the actual request URIs to mimic real client interactions.
  • Use TestRestTemplate class helps in invoking the HTTP requests which are handled by controller class.
  • @Sql annotation helps in populating database with some prerequisite data if test is dependent on it to test the behavior correctly.
  • org.junit.jupiter.api.Test annotations are from Junit 5 and mark the method as test method to run.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.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.boot.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.jdbc.Sql;
import com.howtodoinjava.rest.model.Employee;
import com.howtodoinjava.rest.model.Employees;

@SpringBootTest(classes = SpringBootDemoApplication.class, 
		webEnvironment = WebEnvironment.RANDOM_PORT)
public class EmployeeControllerIntegrationTests 
{
	@LocalServerPort
	private int port;

	@Autowired
	private TestRestTemplate restTemplate;

	@Sql({ "schema.sql", "data.sql" })
	@Test
	public void testAllEmployees() 
	{
		assertTrue(
				this.restTemplate
					.getForObject("http://localhost:" + port + "/employees", Employees.class)
					.getEmployeeList().size() == 3);
	}

	@Test
	public void testAddEmployee() {
		Employee employee = new Employee("Lokesh", "Gupta", "howtodoinjava@gmail.com");
		ResponseEntity<String> responseEntity = this.restTemplate
			.postForEntity("http://localhost:" + port + "/employees", employee, String.class);
		assertEquals(201, responseEntity.getStatusCodeValue());
	}
}

3. Demo

Run the above tests within IDE. I have used Eclipse. The test class start the whole application in embedded server and the execute each test one by one.

image

4. Conclusion

In this spring boot integration testing example with Junit 5, we learned to write tests which test multiple layers of applications in single test. They verify whether the controller and persistence layers work together correctly or not.

We are not required to use actual webserver to run the application while integration testing but we can definitely make use of embedded servers in Spring boot.

Happy Learning !!

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.