Skip to content
thesarfo

Concept

Testing: Controller Layer

Testing Spring MVC controllers with @WebMvcTest and MockMvc, mocking the service layer, and asserting JSON responses with Hamcrest and JsonPath.

views 0

Just like the repository and service layers, the controller layer can be tested too. Testing a controller means mocking the service layer (since the controller talks to the service, the same way the service talks to the repository).

Controller testing itself is done with Spring MVC, via @WebMvcTest.

Hamcrest is a well-known assertion framework, bundled with JUnit, that uses existing predicates (“matcher” classes) to build assertions — instead of JUnit’s many assert* methods, you use one assertThat with the appropriate matcher.

  • is() — verify an expected value/object equals the actual one: assertThat(ACTUAL, is(EXPECTED))

JsonPath is a Java DSL for reading JSON documents, the same way XPath is used with XML. The root member object in JsonPath is always $, whether it’s an object or an array.

@WebMvcTest

Spring Boot’s @WebMvcTest tests Spring MVC controllers. It only loads the web layer (the specified controller and its dependencies) rather than the whole application context, so tests run faster. In a multi-controller app, you can scope it to one controller — @WebMvcTest(HomeController.class). (For full integration testing, use @SpringBootTest instead.)

@WebMvcTest
class EmployeeControllerTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private EmployeeService employeeService;
@Autowired
private ObjectMapper objectMapper;
@Test
void givenEmployeeObject_whenCreateEmployee_thenReturnSavedEmployee() throws Exception {
Employee employee = Employee.builder()
.firstName("travis")
.lastName("scott")
.email("travis@gmail.com")
.build();
given(employeeService.saveEmployee(any(Employee.class)))
.willAnswer((invocation) -> invocation.getArgument(0));
ResultActions response = mockMvc.perform(post("/api/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(employee)));
response.andExpect(status().isCreated())
.andExpect(jsonPath("$.firstName", is(employee.getFirstName())))
.andExpect(jsonPath("$.lastName", is(employee.getLastName())))
.andExpect(jsonPath("$.email", is(employee.getEmail())));
}
@DisplayName("Get All Employees Controller")
@Test
void givenListOfEmployees_whenGetAllEmployees_thenReturnAllEmployees() throws Exception {
List<Employee> listOfEmployees = new ArrayList<>();
listOfEmployees.add(Employee.builder().firstName("ernest").lastName("sarfo").email("sarfo@gmail.com").build());
listOfEmployees.add(Employee.builder().firstName("tony").lastName("stark").email("tonystark@gmail.com").build());
given(employeeService.getAllEmployees()).willReturn(listOfEmployees);
ResultActions response = mockMvc.perform(get("/api/employees"));
response.andExpect(status().isOk())
.andExpect(jsonPath("$.size()", is(listOfEmployees.size())));
}
}