How to Write Integration Test in Spring Boot

Learn to write integration tests for a given REST controller using @SpringBootTest and Junit 5. This technique can be applied to spring boot as well as spring MVC applications.

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 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 its webEnvironment attribute for it. It also creates the ApplicationContext used in the tests.
  • Using an in-memory database is recommended for mimicking the database. Though it is not mandatory, 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. Writing the Integration Tests

2.1. Maven

Start by including the 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>
</dependency>

2.2. REST Controller to Test

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 Example

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 the controller class.
  • @Sql annotation helps in populating the database with some prerequisite data if the 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 a 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());
  }
}

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

3. Conclusion

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

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

Happy Learning !!

Sourcecode Download

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.