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 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
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
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
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;
@Service public 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
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
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
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;
@SpringBootApplication public 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
< 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
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
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: