Spring WebClient Mocking Code Repo (#7525)

*  adding complete code implementation of Hexagonal Architectute example

* refactored the code

* added the webclient mocking

* removed the unwanted repo

* updated the test cases

* removed commented code

* fixed the code review comments

* fixed the test cases

* removed unwanted dependencies

* removed the unwanted project

* fixed the code review comments

* added the mock to the variable name

* added the mock to the variable name

* addressed the code review comments

* removed the unnecessary semi colon

* fixed the compilation issues

* Update spring-5-reactive-client/pom.xml

Co-Authored-By: KevinGilmore <kpg102@gmail.com>

* Update spring-5-reactive-client/pom.xml

Co-Authored-By: KevinGilmore <kpg102@gmail.com>

* added the missing dependency

* fixed the compilation issue in the pom.xml file
This commit is contained in:
Dilip 2019-09-24 15:56:43 -05:00 committed by ashleyfrieze
parent 2757d0a3ad
commit 0a67756dee
6 changed files with 407 additions and 2 deletions

View File

@ -54,6 +54,18 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
<!-- runtime and test scoped -->
<dependency>
@ -88,14 +100,25 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- WebClient logging with Jetty -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.23.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.2.10.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-reactive-httpclient</artifactId>
<version>${jetty-reactive-httpclient.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -108,6 +131,29 @@
<layout>JAR</layout>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

View File

@ -0,0 +1,5 @@
package com.baeldung.reactive.enums;
public enum Role {
ENGINEER, SENIOR_ENGINEER, LEAD_ENGINEER
}

View File

@ -0,0 +1,64 @@
package com.baeldung.reactive.model;
import com.baeldung.reactive.enums.Role;
public class Employee {
private Integer employeeId;
private String firstName;
private String lastName;
private Integer age;
private Role role;
public Employee() {
}
public Employee(Integer employeeId, String firstName, String lastName, Integer age, Role role) {
this.employeeId = employeeId;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.role = role;
}
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer 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;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}

View File

@ -0,0 +1,55 @@
package com.baeldung.reactive.service;
import com.baeldung.reactive.model.Employee;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class EmployeeService {
private WebClient webClient;
public static String PATH_PARAM_BY_ID = "/employee/{id}";
public static String ADD_EMPLOYEE = "/employee";
public EmployeeService(WebClient webClient) {
this.webClient = webClient;
}
public EmployeeService(String baseUrl) {
this.webClient = WebClient.create(baseUrl);
}
public Mono<Employee> getEmployeeById(Integer employeeId) {
return webClient
.get()
.uri(PATH_PARAM_BY_ID, employeeId)
.retrieve()
.bodyToMono(Employee.class);
}
public Mono<Employee> addNewEmployee(Employee newEmployee) {
return webClient
.post()
.uri(ADD_EMPLOYEE)
.syncBody(newEmployee)
.retrieve().
bodyToMono(Employee.class);
}
public Mono<Employee> updateEmployee(Integer employeeId, Employee updateEmployee) {
return webClient
.put()
.uri(PATH_PARAM_BY_ID,employeeId)
.syncBody(updateEmployee)
.retrieve()
.bodyToMono(Employee.class);
}
public Mono<String> deleteEmployeeById(Integer employeeId) {
return webClient
.delete()
.uri(PATH_PARAM_BY_ID,employeeId)
.retrieve()
.bodyToMono(String.class);
}
}

View File

@ -0,0 +1,121 @@
package com.baeldung.reactive.service;
import com.baeldung.reactive.model.Employee;
import com.baeldung.reactive.enums.Role;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.Rule;
import org.junit.jupiter.api.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.*;
class EmployeeServiceIntegrationTest {
public static MockWebServer mockBackEnd;
private EmployeeService employeeService;
private ObjectMapper MAPPER = new ObjectMapper();
@BeforeAll
static void setUp() throws IOException {
mockBackEnd = new MockWebServer();
mockBackEnd.start();
}
@AfterAll
static void tearDown() throws IOException {
mockBackEnd.shutdown();
}
@BeforeEach
void initialize() {
String baseUrl = String.format("http://localhost:%s", mockBackEnd.getPort());
employeeService = new EmployeeService(baseUrl);
}
@Test
void getEmployeeById() throws Exception {
Employee mockEmployee = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(mockEmployee))
.addHeader("Content-Type", "application/json"));
Mono<Employee> employeeMono = employeeService.getEmployeeById(100);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getRole().equals(Role.LEAD_ENGINEER))
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("GET", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());
}
@Test
void addNewEmployee() throws Exception {
Employee newEmployee = new Employee(null, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
Employee webClientResponse = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(webClientResponse))
.addHeader("Content-Type", "application/json"));
Mono<Employee> employeeMono = employeeService.addNewEmployee(newEmployee);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getEmployeeId().equals(100))
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("POST", recordedRequest.getMethod());
assertEquals("/employee", recordedRequest.getPath());
}
@Test
void updateEmployee() throws Exception {
Integer newAge = 33;
String newLastName = "Sandler New";
Employee updateEmployee = new Employee(100, "Adam", newLastName, newAge, Role.LEAD_ENGINEER);
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(updateEmployee))
.addHeader("Content-Type", "application/json"));
Mono<Employee> updatedEmploye = employeeService.updateEmployee(100, updateEmployee);
StepVerifier.create(updatedEmploye)
.expectNextMatches(employee -> employee.getLastName().equals(newLastName) && employee.getAge() == newAge)
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("PUT", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());
}
@Test
void deleteEmployee() throws Exception {
String responseMessage = "Employee Deleted SuccessFully";
Integer employeeId = 100;
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(responseMessage))
.addHeader("Content-Type", "application/json"));
Mono<String> deletedEmployee = employeeService.deleteEmployeeById(employeeId);
StepVerifier.create(deletedEmployee)
.expectNext("\"Employee Deleted SuccessFully\"")
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("DELETE", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());
}
}

View File

@ -0,0 +1,114 @@
package com.baeldung.reactive.service;
import com.baeldung.reactive.model.Employee;
import com.baeldung.reactive.enums.Role;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class EmployeeServiceUnitTest {
EmployeeService employeeService;
@Mock
private WebClient webClientMock;
@Mock
private WebClient.RequestHeadersSpec requestHeadersMock;
@Mock
private WebClient.RequestHeadersUriSpec requestHeadersUriMock;
@Mock
private WebClient.RequestBodySpec requestBodyMock;
@Mock
private WebClient.RequestBodyUriSpec requestBodyUriMock;
@Mock
private WebClient.ResponseSpec responseMock;
@BeforeEach
void setUp() {
employeeService = new EmployeeService(webClientMock);
}
@Test
void givenEmployeeId_whenGetEmployeeById_thenReturnEmployee() {
Integer employeeId = 100;
Employee mockEmployee = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
when(webClientMock.get()).thenReturn(requestHeadersUriMock);
when(requestHeadersUriMock.uri("/employee/{id}", employeeId)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(Employee.class)).thenReturn(Mono.just(mockEmployee));
Mono<Employee> employeeMono = employeeService.getEmployeeById(employeeId);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getRole().equals(Role.LEAD_ENGINEER))
.verifyComplete();
}
@Test
void givenEmployee_whenAddEmployee_thenAddNewEmployee() {
Employee newEmployee = new Employee(null, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
Employee webClientResponse = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
when(webClientMock.post()).thenReturn(requestBodyUriMock);
when(requestBodyUriMock.uri(EmployeeService.ADD_EMPLOYEE)).thenReturn(requestBodyMock);
when(requestBodyMock.syncBody(newEmployee)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(Employee.class)).thenReturn(Mono.just(webClientResponse));
Mono<Employee> employeeMono = employeeService.addNewEmployee(newEmployee);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getEmployeeId().equals(100))
.verifyComplete();
}
@Test
void givenEmployee_whenupdateEmployee_thenUpdatedEmployee() {
Integer newAge = 33;
String newLastName = "Sandler New";
Employee updateEmployee = new Employee(100, "Adam", newLastName, newAge, Role.LEAD_ENGINEER);
when(webClientMock.put()).thenReturn(requestBodyUriMock);
when(requestBodyUriMock.uri(EmployeeService.PATH_PARAM_BY_ID, 100)).thenReturn(requestBodyMock);
when(requestBodyMock.syncBody(updateEmployee)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(Employee.class)).thenReturn(Mono.just(updateEmployee));
Mono<Employee> updatedEmployee = employeeService.updateEmployee(100, updateEmployee);
StepVerifier.create(updatedEmployee)
.expectNextMatches(employee -> employee.getLastName().equals(newLastName) && employee.getAge() == newAge)
.verifyComplete();
}
@Test
void givenEmployee_whenDeleteEmployeeById_thenDeleteSuccessful() {
String responseMessage = "Employee Deleted SuccessFully";
when(webClientMock.delete()).thenReturn(requestHeadersUriMock);
when(requestHeadersUriMock.uri(EmployeeService.PATH_PARAM_BY_ID, 100)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(String.class)).thenReturn(Mono.just(responseMessage));
Mono<String> deletedEmployee = employeeService.deleteEmployeeById(100);
StepVerifier.create(deletedEmployee)
.expectNext(responseMessage)
.verifyComplete();
}
}