Test a Spring Boot REST Controller with JUnit 5

Learn to unit test given Spring rest controller APIs using Junit 5 and mockito. This technique can be applied to spring boot as well as spring MVC applications, both.

Spring rest controller unit test example

A unit test for a Spring Boot REST controller checks only the code of the controller class, with all its dependencies replaced by mocks. We write such a test with JUnit 5, Mockito and MockMvc, which @WebMvcTest autoconfigures. The same technique applies to Spring Boot and Spring MVC applications.

1. Setup

First, we include the latest version of the spring-boot-starter-test starter dependency.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

We write the unit tests for a Spring Boot REST controller with three details that affect the tests.

  • The controller depends on the EmployeeDAO class for persistence.
  • The method addEmployee() needs access to the request context through ServletUriComponentsBuilder.
  • The method addEmployee() returns the HTTP status and header through the ResponseEntity class.
@RestController
@RequestMapping(path = "/employees")
public class EmployeeController 
{
    @Autowired
    private EmployeeDAO employeeDao;
 
    @GetMapping(path="/", produces = "application/json")
    public Employees getEmployees() {
        return employeeDao.getAllEmployees();
    }
     
    @PostMapping(path= "/", consumes = "application/json", produces = "application/json")
    public ResponseEntity<Object> addEmployee(@RequestBody Employee employee) {       
                 
        employeeDao.addEmployee(employee);
         
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                                    .path("/{id}")
                                    .buildAndExpand(employee.getId())
                                    .toUri();
         
        return ResponseEntity.created(location).build();
    }
}

2. Unit Testing using Spring Boot @WebMvcTest

The @WebMvcTest annotation is used to unit test the Spring MVC components (@Controller and @ControllerAdvice). It disables the full auto-configuration and configures only Spring Security and MockMvc.

@WebMvcTest(EmployeeRESTController.class)
public class TestEmployeeRESTController {
 
  @Autowired
  private MockMvc mvc;

  @MockBean
  private EmployeeService employeeService; // Mock the service used by the controller
}

Finally, we use the MockMvc bean to invoke the APIs and verify the results.

@Test
public void getAllEmployeesAPI() throws Exception {

  // Mock service response
  List<EmployeeVO> mockEmployees = List.of(
      new EmployeeVO(1L, "John", "Doe", "john.doe@mail.com"),
      new EmployeeVO(2L, "Jane", "Smith", "jane.smith@mail.com")
  );

  Mockito.when(employeeService.getAllEmployees()).thenReturn(mockEmployees);

  mvc.perform(MockMvcRequestBuilders
          .get("/employees")
          .accept(MediaType.APPLICATION_JSON))
      .andDo(print())
      .andExpect(status().isOk())
      .andExpect(MockMvcResultMatchers.jsonPath("$[0].employeeId").value(1L))
      .andExpect(MockMvcResultMatchers.jsonPath("$[1].employeeId").value(2L));
}

@Test
public void createEmployeeAPI() throws Exception {

  EmployeeVO input = new EmployeeVO(null, "firstName", "lastName", "admin@mail.com");
  EmployeeVO saved = new EmployeeVO(100L, "firstName", "lastName", "admin@mail.com");

  Mockito.when(employeeService.createEmployee(Mockito.any(EmployeeVO.class))).thenReturn(saved);

  mvc.perform(MockMvcRequestBuilders
          .post("/employees")
          .content(asJsonString(input))
          .contentType(MediaType.APPLICATION_JSON)
          .accept(MediaType.APPLICATION_JSON))
      .andExpect(status().isCreated())
      .andExpect(MockMvcResultMatchers.jsonPath("$.employeeId").value(100L));
}

public static String asJsonString(final Object obj) {

  try {
      return new ObjectMapper().writeValueAsString(obj);
  } catch (Exception e) {
      throw new RuntimeException(e);
  }
}

3. Unit Testing Spring Controller using Mockito

We add the following dependency to use Mockito with Spring.

<!-- Mockito extention -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>

The test class contains unit tests for the Spring Boot REST controller, written with the Mockito APIs. It works as follows.

  • The @Mock annotation creates a mock object for the EmployeeDAO dependency.
  • The @InjectMocks annotation creates the EmployeeController instance and injects the mocked employeeDAO instance.
  • The extension MockitoExtension initializes the mocks and handles strict stubbings. It is the JUnit Jupiter equivalent of the JUnit 4 MockitoJUnitRunner.
  • The classes MockHttpServletRequest and RequestContextHolder supply the request context where the code under test needs it.
  • The methods org.mockito.Mockito.when() and thenReturn() mock the desired behavior.
  • Finally, the JUnit 5 assertions compare the test results with the expected results.

Read More : Mockito annotations – @Mock, @Spy, @Captor, @InjectMocks

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import com.howtodoinjava.rest.controller.EmployeeController;
import com.howtodoinjava.rest.dao.EmployeeRepository;
import com.howtodoinjava.rest.model.Employee;
import com.howtodoinjava.rest.model.Employees;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@ExtendWith(MockitoExtension.class)
public class EmployeeControllerTest
{
    @InjectMocks
    EmployeeController employeeController;

    @Mock
    EmployeeDAO employeeDAO;

    @Test
    public void testAddEmployee()
    {
        MockHttpServletRequest request = new MockHttpServletRequest();
        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));

        when(employeeDAO.addEmployee(any(Employee.class))).thenReturn(true);

        Employee employee = new Employee(1, "Lokesh", "Gupta", "howtodoinjava@gmail.com");
        ResponseEntity<Object> responseEntity = employeeController.addEmployee(employeeToAdd);

        assertThat(responseEntity.getStatusCodeValue()).isEqualTo(201);
        assertThat(responseEntity.getHeaders().getLocation().getPath()).isEqualTo("/1");
    }

    @Test
    public void testFindAll() {
        Employee employee1 = new Employee(1, "Lokesh", "Gupta", "howtodoinjava@gmail.com");
        Employee employee2 = new Employee(2, "Alex", "Gussin", "example@gmail.com");
        Employees employees = new Employees();
        employees.setEmployeeList(Arrays.asList(employee1, employee2));

        when(employeeDAO.getAllEmployees()).thenReturn(employees);

        Employees result = employeeController.getEmployees();

        assertThat(result.getEmployeeList().size()).isEqualTo(2);
        assertThat(result.getEmployeeList().get(0).getFirstName()).isEqualTo(employee1.getFirstName());
        assertThat(result.getEmployeeList().get(1).getFirstName()).isEqualTo(employee2.getFirstName());
    }
}

4. Best Practices

When we write a JUnit test for a REST controller method, we follow these practices.

  • A unit test tests only the code of the controller class, so we mock all the dependencies that the controller class injects and uses.
  • If the test uses outer dependencies, such as a database or a network, it is an integration test, not a unit test.
  • A web server slows down unit tests, so we do not start one.
  • Each unit test is independent of the other tests.
  • Unit tests are fast by definition.

5. Conclusion

In this Spring Boot REST controller unit testing example with JUnit 5 and Mockito, we wrote tests that mock all the controller dependencies and test only the controller code.

We also saw that unit tests do not use an actual web server. Only integration tests need the server.

Happy Learning !!

Source Code on Github

Leave a Comment

  1. with this test how can it work when you use MvcMock without Mocking Repository
    @Test
    public void getAllEmployeesAPI() throws Exception
    {
    mvc.perform(MockMvcRequestBuilders
    .get(“/employees”)
    .accept(MediaType.APPLICATION_JSON))
    .andDo(print())
    .andExpect(status().isOk())
    .andExpect(MockMvcResultMatchers.jsonPath(“$.employees”).exists())
    .andExpect(MockMvcResultMatchers.jsonPath(“$.employees[*].employeeId”).isNotEmpty());
    }

Comments are closed.

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.