Spring Boot MockMVC Testing with Example Project

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:
				
					


