Test for updates java

Java Guides

In this tutorial, we will learn how to perform unit testing Spring boot CRUD RESTful web services using JUnit 5 and Mockito framework.

Spring boot provides spring-boot-starter-test dependency for unit testing and integration testing of Spring boot application:

  org.springframework.boot spring-boot-starter-test test 

The Spring Boot Starter Test dependency is a primary dependency for testing the Spring Boot Applications. It holds all the necessary elements required for the testing.

For the Unit testing controller layer REST APIs, we gonna use the following testing libraries:

  • JUnit 5 Framework
  • Mockito 4 (Latest)
  • Hamcrest framework
  • AssertJ Library
  • JsonPath Library

JUnit 5 Framework

The current version of JUnit is 5+. The main goal of JUnit 5 is to support Java 8 and above, as well as enable many different styles of testing.

Mockito 4 (Latest)

Mockito is a mocking framework. It is a Java-based library used to create simple and basic test APIs for performing unit testing of Java applications.

The main purpose of using the Mockito framework is to simplify the development of a test by mocking external dependencies and using them in the test code.

Hamcrest framework

Hamcrest is the well-known framework used for unit testing in the Java ecosystem. It’s bundled in JUnit and simply put, it uses existing predicates – called matcher classes – for making assertions.

Читайте также:  Профилирование кода python cprofile

Hamcrest is commonly used with JUnit and other testing frameworks for making assertions. Specifically, instead of using JUnit’s numerous assert methods, we only use the API’s single assertThat statement with appropriate matchers.

AssertJ Library

AssertJ is a Java library that provides a rich set of assertions and truly helpful error messages, improves test code readability, and is designed to be super easy to use within your favorite IDE.

Spring boot starter test dependency internally provides assertj-core dependency so we don’t have to add assertj-core dependency manually in our Spring boot project.

JsonPath Library

JsonPath expressions always refer to a JSON structure in the same way as XPath expressions are used in combination with an XML document. The «root member object» in JsonPath is always referred to as $ regardless of whether it is an object or array.

@WebMvcTest Annotation

SpringBoot provides @WebMvcTest annotation to test Spring MVC Controllers. Also, @WebMvcTest based test run faster because it will load only the specified controller and its dependencies only without loading the entire application.

Spring Boot instantiates only the web layer rather than the whole application context. In an application with multiple controllers, you can even ask for only one to be instantiated by using, for example, @WebMvcTest(HomeController.class).

Tools and technologies used

  • Java 11+
  • Spring Boot
  • Lombok
  • JUnit 5 Framework
  • Hamcrest
  • AssertJ
  • JsonPath
  • Mockito
  • IntelliJ IDEA
  • Docker
  • Maven

1. Create Spring Boot Application

2. Maven Dependencies

  org.springframework.boot spring-boot-starter-data-jpa  org.springframework.boot spring-boot-starter-web  org.projectlombok lombok true  org.springframework.boot spring-boot-starter-test test 

3. Create JPA Entity

import lombok.*; import javax.persistence.*; @Setter @Getter @AllArgsConstructor @NoArgsConstructor @Builder @Entity @Table(name = "employees") public class Employee < @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "first_name", nullable = false) private String firstName; @Column(name = "last_name", nullable = false) private String lastName; @Column(nullable = false) private String email; >

@GeneratedValue annotation is used to define the primary key generation strategy. In the above case, we have declared the primary key to be an Auto Increment field.

@Column annotation is used to define the properties of the column that will be mapped to the annotated field. You can define several properties like name, length, nullable, updateable, etc.

4. Create Repository Layer

import net.javaguides.springboot.model.Employee; import org.springframework.data.jpa.repository.JpaRepository; public interface EmployeeRepository extends JpaRepositoryEmployee, Long>

5. Create Service Layer

EmployeeService

import net.javaguides.springboot.model.Employee; import java.util.List; import java.util.Optional; public interface EmployeeService < Employee saveEmployee(Employee employee); List getAllEmployees(); Optional getEmployeeById(long id); Employee updateEmployee(Employee updatedEmployee); void deleteEmployee(long id); >

EmployeeServiceImpl

import net.javaguides.springboot.exception.ResourceNotFoundException; import net.javaguides.springboot.model.Employee; import net.javaguides.springboot.repository.EmployeeRepository; import net.javaguides.springboot.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; @Service public class EmployeeServiceImpl implements EmployeeService < private EmployeeRepository employeeRepository; public EmployeeServiceImpl(EmployeeRepository employeeRepository) < this.employeeRepository = employeeRepository; > @Override public Employee saveEmployee(Employee employee)  < OptionalsavedEmployee = employeeRepository.findByEmail(employee.getEmail()); if(savedEmployee.isPresent())< throw new ResourceNotFoundException("Employee already exist with given email:" + employee.getEmail()); > return employeeRepository.save(employee); > @Override public List getAllEmployees() < return employeeRepository.findAll(); > @Override public Optional getEmployeeById(long id) < return employeeRepository.findById(id); > @Override public Employee updateEmployee(Employee updatedEmployee) < return employeeRepository.save(updatedEmployee); > @Override public void deleteEmployee(long id)  < employeeRepository.deleteById(id); >>

6. Controller Layer — CRUD REST APIs

import net.javaguides.springboot.model.Employee; import net.javaguides.springboot.service.EmployeeService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/employees") public class EmployeeController < private EmployeeService employeeService; public EmployeeController(EmployeeService employeeService) < this.employeeService = employeeService; > @PostMapping @ResponseStatus(HttpStatus.CREATED) public Employee createEmployee(@RequestBody Employee employee)< return employeeService.saveEmployee(employee); > @GetMapping public List getAllEmployees( )< return employeeService.getAllEmployees(); > @GetMapping("") public ResponseEntity getEmployeeById(@PathVariable("id") long employeeId)< return employeeService.getEmployeeById(employeeId) .map(ResponseEntity::ok) .orElseGet(() -> ResponseEntity.notFound().build()); > @PutMapping("") public ResponseEntity updateEmployee(@PathVariable("id") long employeeId, @RequestBody Employee employee)< return employeeService.getEmployeeById(employeeId) .map(savedEmployee -> < savedEmployee.setFirstName(employee.getFirstName()); savedEmployee.setLastName(employee.getLastName()); savedEmployee.setEmail(employee.getEmail()); Employee updatedEmployee = employeeService.updateEmployee(savedEmployee); return new ResponseEntity<>(updatedEmployee, HttpStatus.OK); >) .orElseGet(() -> ResponseEntity.notFound().build()); > @DeleteMapping("") public ResponseEntityString> deleteEmployee(@PathVariable("id") long employeeId)< employeeService.deleteEmployee(employeeId); return new ResponseEntityString>("Employee deleted successfully!.", HttpStatus.OK); > >

7. Writing Unit Tests for CRUD REST API’s

Now, let’s create Unit test cases for CRUD REST APIs. We gonna use the @WebMvcTest annotation to load only EmployeeController class.

import com.fasterxml.jackson.databind.ObjectMapper; import net.javaguides.springboot.model.Employee; import net.javaguides.springboot.service.EmployeeService; 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.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import java.util.ArrayList; import java.util.List; import java.util.Optional; import static org.hamcrest.CoreMatchers.is; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.BDDMockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @WebMvcTest public class EmployeeControllerTests < @Autowired private MockMvc mockMvc; @MockBean private EmployeeService employeeService; @Autowired private ObjectMapper objectMapper; @Test public void givenEmployeeObject_whenCreateEmployee_thenReturnSavedEmployee() throws Exception< // given - precondition or setup Employee employee = Employee.builder() .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); given(employeeService.saveEmployee(any(Employee.class))) .willAnswer((invocation)->invocation.getArgument(0)); // when - action or behaviour that we are going test ResultActions response = mockMvc.perform(post("/api/employees") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(employee))); // then - verify the result or output using assert statements response.andDo(print()). andExpect(status().isCreated()) .andExpect(jsonPath("$.firstName", is(employee.getFirstName()))) .andExpect(jsonPath("$.lastName", is(employee.getLastName()))) .andExpect(jsonPath("$.email", is(employee.getEmail()))); > // JUnit test for Get All employees REST API @Test public void givenListOfEmployees_whenGetAllEmployees_thenReturnEmployeesList() throws Exception < // given - precondition or setup ListlistOfEmployees = new ArrayList<>(); listOfEmployees.add(Employee.builder().firstName("Ramesh").lastName("Fadatare").email("ramesh@gmail.com").build()); listOfEmployees.add(Employee.builder().firstName("Tony").lastName("Stark").email("tony@gmail.com").build()); given(employeeService.getAllEmployees()).willReturn(listOfEmployees); // when - action or the behaviour that we are going test ResultActions response = mockMvc.perform(get("/api/employees")); // then - verify the output response.andExpect(status().isOk()) .andDo(print()) .andExpect(jsonPath("$.size()", is(listOfEmployees.size()))); > // positive scenario - valid employee id // JUnit test for GET employee by id REST API @Test public void givenEmployeeId_whenGetEmployeeById_thenReturnEmployeeObject() throws Exception< // given - precondition or setup long employeeId = 1L; Employee employee = Employee.builder() .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); given(employeeService.getEmployeeById(employeeId)).willReturn(Optional.of(employee)); // when - action or the behaviour that we are going test ResultActions response = mockMvc.perform(get("/api/employees/", employeeId)); // then - verify the output response.andExpect(status().isOk()) .andDo(print()) .andExpect(jsonPath("$.firstName", is(employee.getFirstName()))) .andExpect(jsonPath("$.lastName", is(employee.getLastName()))) .andExpect(jsonPath("$.email", is(employee.getEmail()))); > // negative scenario - valid employee id // JUnit test for GET employee by id REST API @Test public void givenInvalidEmployeeId_whenGetEmployeeById_thenReturnEmpty() throws Exception< // given - precondition or setup long employeeId = 1L; Employee employee = Employee.builder() .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); given(employeeService.getEmployeeById(employeeId)).willReturn(Optional.empty()); // when - action or the behaviour that we are going test ResultActions response = mockMvc.perform(get("/api/employees/", employeeId)); // then - verify the output response.andExpect(status().isNotFound()) .andDo(print()); > // JUnit test for update employee REST API - positive scenario @Test public void givenUpdatedEmployee_whenUpdateEmployee_thenReturnUpdateEmployeeObject() throws Exception < // given - precondition or setup long employeeId = 1L; Employee savedEmployee = Employee.builder() .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); Employee updatedEmployee = Employee.builder() .firstName("Ram") .lastName("Jadhav") .email("ram@gmail.com") .build(); given(employeeService.getEmployeeById(employeeId)).willReturn(Optional.of(savedEmployee)); given(employeeService.updateEmployee(any(Employee.class))) .willAnswer((invocation)->invocation.getArgument(0)); // when - action or the behaviour that we are going test ResultActions response = mockMvc.perform(put("/api/employees/", employeeId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updatedEmployee))); // then - verify the output response.andExpect(status().isOk()) .andDo(print()) .andExpect(jsonPath("$.firstName", is(updatedEmployee.getFirstName()))) .andExpect(jsonPath("$.lastName", is(updatedEmployee.getLastName()))) .andExpect(jsonPath("$.email", is(updatedEmployee.getEmail()))); > // JUnit test for update employee REST API - negative scenario @Test public void givenUpdatedEmployee_whenUpdateEmployee_thenReturn404() throws Exception < // given - precondition or setup long employeeId = 1L; Employee savedEmployee = Employee.builder() .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); Employee updatedEmployee = Employee.builder() .firstName("Ram") .lastName("Jadhav") .email("ram@gmail.com") .build(); given(employeeService.getEmployeeById(employeeId)).willReturn(Optional.empty()); given(employeeService.updateEmployee(any(Employee.class))) .willAnswer((invocation)->invocation.getArgument(0)); // when - action or the behaviour that we are going test ResultActions response = mockMvc.perform(put("/api/employees/", employeeId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updatedEmployee))); // then - verify the output response.andExpect(status().isNotFound()) .andDo(print()); > // JUnit test for delete employee REST API @Test public void givenEmployeeId_whenDeleteEmployee_thenReturn200() throws Exception< // given - precondition or setup long employeeId = 1L; willDoNothing().given(employeeService).deleteEmployee(employeeId); // when - action or the behaviour that we are going test ResultActions response = mockMvc.perform(delete("/api/employees/", employeeId)); // then - verify the output response.andExpect(status().isOk()) .andDo(print()); > > 

We are using @MockBean annotation to add mock objects to the Spring application context. The mock will replace any existing bean of the same type in the application context.

The @MockBean annotation tells Spring to create a mock instance of EmployeeService and add it to the application context so that it’s injected into EmployeeController. We have a handle on it in the test so that we can define its behavior before running each test.

Источник

Оцените статью