In a Spring Boot project, we have to test the web layer. For that, we can use MockMVC. In this tutorial, let us see how to do that by having a sample GeekEmployee bean and writing the business logic as well as the test cases for it.
Example Project
Project Structure:
This is a maven project. Let’s start with
pom.xml
XML
<?xml version="1.0" encoding="UTF-8"?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> </parent> <groupId>com.gfg</groupId> <artifactId>test-springmvc</artifactId> <version>0.0.1-SNAPSHOT</version> <name>test-springmvc</name> <description>Sample Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <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> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
First, let’s go with the bean class
GeekEmployee.java
Java
public class GeekEmployee { private Long employeeId; private String firstName; private String lastName; private int salary; public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public GeekEmployee(String firstName, String lastName, int salary) { this.firstName = firstName; this.lastName = lastName; this.salary = salary; } public Long getEmployeeId() { return employeeId; } public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } |
As employeeId is auto-generated, let us create that via
GeekEmployeeIdGenerator.java
Java
public class GeekEmployeeIdGenerator { private static long employeeId = 1000; public static synchronized long value() { return employeeId++; }} |
Service file where we can write our business logic
GeekEmployeeService.java
Java
import java.util.Collection;import java.util.HashMap;import java.util.Map;import java.util.Optional; import org.springframework.stereotype.Service; import com.gfg.gfgsample.domain.GeekEmployee;import com.gfg.gfgsample.util.GeekEmployeeIdGenerator; @Servicepublic class GeekEmployeeService { Map<Long, GeekEmployee> geekEmployees = new HashMap<>(); // Return all geekEmployees public Collection<GeekEmployee> findAll(){ return geekEmployees.values(); } // Find the geekEmployee with this id public Optional<GeekEmployee> findById(Long employeeId) { GeekEmployee geekEmployee = null; if (geekEmployees.containsKey(employeeId)) geekEmployee = geekEmployees.get(employeeId); return Optional.ofNullable(geekEmployee); } // Save a new GeekEmployee public GeekEmployee save(GeekEmployee geekEmployee) { geekEmployee.setEmployeeId(GeekEmployeeIdGenerator.value()); geekEmployees.put(geekEmployee.getEmployeeId(), geekEmployee); return geekEmployee; } // Update the GeekEmployee with this id public Optional<GeekEmployee> update(GeekEmployee geekEmployee) { GeekEmployee geekEmployee1 = geekEmployees.get(geekEmployee.getEmployeeId()); if (geekEmployee1 != null) { geekEmployees.put(geekEmployee.getEmployeeId(), geekEmployee); geekEmployee1 = geekEmployees.get(geekEmployee.getEmployeeId()); } return Optional.ofNullable(geekEmployee1); } // Delete GeekEmployee with this id public Optional<GeekEmployee> delete(Long employeeId) { GeekEmployee geekEmployee1 = geekEmployees.get(employeeId); if (geekEmployee1 != null) { geekEmployees.remove(employeeId); } return Optional.ofNullable(geekEmployee1); } } |
GeekEmployeeMvcController.java
Java
package com.gfg.gfgsample.web; import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping; import com.gfg.gfgsample.service.GeekEmployeeService; @Controller@RequestMapping("mvc")public class GeekEmployeeMvcController { private final GeekEmployeeService geekEmployeeService; public GeekEmployeeMvcController(GeekEmployeeService geekEmployeeService) { this.geekEmployeeService = geekEmployeeService; } @GetMapping("geekemployees") public String getGeekEmployees(Model model) { model.addAttribute("geekemployees", geekEmployeeService.findAll()); return "geekemployee-list"; } } |
GeekEmployeeRestController.java
Java
import java.net.URI;import java.util.Collection; import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseStatus;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.gfg.gfgsample.domain.GeekEmployee;import com.gfg.gfgsample.service.GeekEmployeeService;import com.gfg.gfgsample.util.GeekEmployeeIdGenerator; @RestController@RequestMapping("geekemployees")public class GeekEmployeeRestController { private final GeekEmployeeService geekEmployeeService; public GeekEmployeeRestController(GeekEmployeeService service) { this.geekEmployeeService = service; } @GetMapping Collection<GeekEmployee> readGeekEmployees(){ return this.geekEmployeeService.findAll(); } @GetMapping("/{id}") GeekEmployee readGeekEmployee(@PathVariable Long id) { return this.geekEmployeeService.findById(id) .orElseThrow(GeekEmployeeNotFoundException::new); } @PostMapping ResponseEntity<?> addEmployee(@RequestBody GeekEmployee geekEmployee){ // Hack to get Mockito test to work // Will fix this soon // When not running JUnit tests // These statements should be commented out // and the statements below should be uncommented this.geekEmployeeService.save(geekEmployee); URI location = ServletUriComponentsBuilder .fromCurrentRequest() .path("/{id}") .buildAndExpand(GeekEmployeeIdGenerator.value()) .toUri(); return ResponseEntity.created(location).build(); } @PutMapping GeekEmployee updateEmployee(@RequestBody GeekEmployee geekEmployee) { return this.geekEmployeeService.update(geekEmployee) .orElseThrow(GeekEmployeeNotFoundException::new); } @DeleteMapping("/{id}") void deleteStudent(@PathVariable Long id) { this.geekEmployeeService.delete(id) .orElseThrow(GeekEmployeeNotFoundException::new); } @ResponseStatus(HttpStatus.NOT_FOUND) class GeekEmployeeNotFoundException extends RuntimeException { private static final long serialVersionUID = 1L; public GeekEmployeeNotFoundException() { super("Employee does not exist"); } }} |
Start up the file that can run as the java application
TestSpringmvcApplication.java
Java
import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean; import com.gfg.gfgsample.domain.GeekEmployee;import com.gfg.gfgsample.service.GeekEmployeeService; @SpringBootApplicationpublic class TestSpringmvcApplication { public static void main(String[] args) { SpringApplication.run(TestSpringmvcApplication.class, args); } @Bean CommandLineRunner init(GeekEmployeeService geekEmployeeService) { return args -> { geekEmployeeService.save(new GeekEmployee("Rachel", "Green", 100000)); geekEmployeeService.save(new GeekEmployee("Monica", "Geller", 40000)); geekEmployeeService.save(new GeekEmployee("Phoebe", "", 45000)); }; }} |
geekemployee-list.html
HTML
<head> <title>Employee List</title> <!-- Latest compiled and minified CSS --> <link rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" /> </head> <body> <div class="container"> <div class="page-header"> <h1>Employee List</h1> </div> <div class="container"> <div class="column"> <table class="table datatable"> <tr> <th>First Name</th> <th>Last Name</th> <th>Salary</th> </tr> <tr th:each="geekemployee : ${geekemployees}"> <td th:text="${geekemployee.firstName}">Joe</td> <td th:text="${geekemployee.lastName}">Tribiani</td> <td th:text="${geekemployee.salary}">100000</td> </tr> </table> </div> </div> </div> </body></html> |
After running the spring application, our console is as follows
Output on mvc/geekemployees
Testing Part:
GeekEmployeeMvcWebTest.java
Java
import static org.mockito.Mockito.when;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; import java.util.List; import org.hamcrest.Matchers;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;import org.springframework.boot.test.mock.mockito.MockBean;import org.springframework.test.web.servlet.MockMvc; import com.gfg.gfgsample.domain.GeekEmployee;import com.gfg.gfgsample.service.GeekEmployeeService; @WebMvcTest(controllers = GeekEmployeeMvcController.class)class GeekEmployeeMvcWebTest { @Autowired MockMvc mockMvc; @MockBean GeekEmployeeService geekEmployeeService; @Test void checkForGeekEmployeeListView() throws Exception { GeekEmployee ge1 = new GeekEmployee("Rachel", "Green", 50000); GeekEmployee ge2 = new GeekEmployee("Monica", "Geller", 40000); GeekEmployee ge3 = new GeekEmployee("Phoebe", "", 45000); List<GeekEmployee> geekEmployeeList = List.of(ge1, ge2, ge3); when(geekEmployeeService.findAll()).thenReturn(geekEmployeeList); this.mockMvc.perform(get("/mvc/geekemployees")) .andExpect(status().isOk()) .andExpect(view().name("geekemployee-list")) .andExpect(model().attribute("geekemployees", geekEmployeeList)) .andExpect(model().attribute("geekemployees", Matchers.hasSize(3))) .andDo(print()); } } |
Testcase Output:
GeekEmployeeRestWebTest.java
Java
import static org.hamcrest.CoreMatchers.containsString;import static org.mockito.Mockito.when;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.Optional; import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;import org.springframework.boot.test.mock.mockito.MockBean;import org.springframework.test.web.servlet.MockMvc; import com.gfg.gfgsample.domain.GeekEmployee;import com.gfg.gfgsample.service.GeekEmployeeService; @WebMvcTest(controllers = GeekEmployeeRestController.class)class GeekEmployeeRestWebTest { @Autowired MockMvc mockMvc; @MockBean GeekEmployeeService geekEmployeeService; @Test void whenReadGeekEmployee_returnJsonContent() throws Exception { GeekEmployee rachel = new GeekEmployee("Rachel", "Green", 100000); rachel.setEmployeeId(1000L); when(geekEmployeeService.findById(1000L)).thenReturn(Optional.of(rachel)); this.mockMvc.perform(get("/geekemployees/1000")) .andExpect(status().isOk()) .andExpect(content().string(containsString( "{\"employeeId\":1000,\"firstName\":\"Rachel\",\"lastName\":\"Green\",\"salary\":100000}"))) .andDo(print()); }} |
Testcase Output:
