Bael 5265: OpenFeign and Spring Exception handler (#12468)
* Implemented cassandra batch query * Added netty version param * Reformatted correctly * Reformatted correctly * Reformatted correctly * Formatting fix resolved * Formatting fix resolved * Removed unused method * Refactored method for better readability * tab spaces corrected * Added http headers in feign * Updated code * Updated code * Removed unused code * Removed unused logger code * Implemented Interceptor and logging related code review * Added AuthService Code * Removed toString method * Removed unnecessary declaration * Removed new line * Added feign headers log as well * Moved to Authorisation package for better naming * spaces removed * @Override included * [Saikat]| Adding the OpenFeign exception default and customizable exception handler and test cases * [Saikat]| Test name updated * [Saikat]| Test name updated * [Saikat]| Removed unused import * [Saikat]| Refactored code * [Saikat]| Reformatted the test codes * [Saikat]| Removed unnecessary code * [Saikat]| Controller test fixed for data error * [Saikat]| Code review fixes * [Saikat]| Code review fixes * [Saikat]| Removed unnecessary annotation * [Saikat]| Added extends ResponseEntityExceptionHandler * [Saikat]| Code review implemented * [Saikat]| Indentation added * Removed empty line before method start Co-authored-by: saikat chakraborty <saikat.chakraborty@tesco.com>
This commit is contained in:
parent
e7c4294e3e
commit
8a79e04d39
@ -61,6 +61,12 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.tomakehurst</groupId>
|
||||||
|
<artifactId>wiremock-jre8</artifactId>
|
||||||
|
<version>2.33.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.client;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.config.FeignConfig;
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.model.Product;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
@FeignClient(name = "product-client-2", url = "http://localhost:8081/product/", configuration = FeignConfig.class)
|
||||||
|
public interface ProductClient {
|
||||||
|
|
||||||
|
@RequestMapping(value = "{id}", method = RequestMethod.GET)
|
||||||
|
Product getProduct(@PathVariable(value = "id") String id);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.config;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.exception.BadRequestException;
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.exception.ProductNotFoundException;
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.exception.ProductServiceNotAvailableException;
|
||||||
|
import feign.Response;
|
||||||
|
import feign.codec.ErrorDecoder;
|
||||||
|
|
||||||
|
public class CustomErrorDecoder implements ErrorDecoder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Exception decode(String methodKey, Response response) {
|
||||||
|
switch (response.status()){
|
||||||
|
case 400:
|
||||||
|
return new BadRequestException();
|
||||||
|
case 404:
|
||||||
|
return new ProductNotFoundException("Product not found");
|
||||||
|
case 503:
|
||||||
|
return new ProductServiceNotAvailableException("Product Api is unavailable");
|
||||||
|
default:
|
||||||
|
return new Exception("Exception while getting product details");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.config;
|
||||||
|
|
||||||
|
import feign.Logger;
|
||||||
|
import feign.codec.ErrorDecoder;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
public class FeignConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Logger.Level feignLoggerLevel() {
|
||||||
|
return Logger.Level.FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ErrorDecoder errorDecoder() {
|
||||||
|
return new CustomErrorDecoder();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.controller;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.client.ProductClient;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.model.Product;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
|
||||||
|
@RestController("product_controller2")
|
||||||
|
@RequestMapping(value = "myapp2")
|
||||||
|
public class ProductController {
|
||||||
|
|
||||||
|
private final ProductClient productClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ProductController(ProductClient productClient) {
|
||||||
|
this.productClient = productClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/product/{id}")
|
||||||
|
public Product getProduct(@PathVariable String id) {
|
||||||
|
return productClient.getProduct(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.exception;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class ErrorResponse {
|
||||||
|
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
|
||||||
|
private Date timestamp;
|
||||||
|
|
||||||
|
@JsonProperty(value = "code")
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
@JsonProperty(value = "status")
|
||||||
|
private String status;
|
||||||
|
@JsonProperty(value = "message")
|
||||||
|
private String message;
|
||||||
|
@JsonProperty(value = "details")
|
||||||
|
private String details;
|
||||||
|
|
||||||
|
public ErrorResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorResponse(HttpStatus httpStatus, String message, String details) {
|
||||||
|
timestamp = new Date();
|
||||||
|
this.code = httpStatus.value();
|
||||||
|
this.status = httpStatus.name();
|
||||||
|
this.message = message;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDetails() {
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.exception;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.exception.BadRequestException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class ProductExceptionHandler extends ResponseEntityExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler({ProductServiceNotAvailableException.class})
|
||||||
|
public ResponseEntity<ErrorResponse> handleProductServiceNotAvailableException(ProductServiceNotAvailableException exception, WebRequest request) {
|
||||||
|
return new ResponseEntity<>(new ErrorResponse(
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
exception.getMessage(),
|
||||||
|
request.getDescription(false)),
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler({ProductNotFoundException.class})
|
||||||
|
public ResponseEntity<ErrorResponse> handleProductNotFoundException(ProductNotFoundException exception, WebRequest request) {
|
||||||
|
return new ResponseEntity<>(new ErrorResponse(
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
exception.getMessage(),
|
||||||
|
request.getDescription(false)),
|
||||||
|
HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleGenericException(Exception exception, WebRequest request) {
|
||||||
|
return new ResponseEntity<>(new ErrorResponse(
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
exception.getMessage(),
|
||||||
|
request.getDescription(false)),
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.exception;
|
||||||
|
|
||||||
|
public class ProductNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
|
public ProductNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.exception;
|
||||||
|
|
||||||
|
public class ProductServiceNotAvailableException extends RuntimeException {
|
||||||
|
|
||||||
|
public ProductServiceNotAvailableException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.defaulterrorhandling.client;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.config.FeignConfig;
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.model.Product;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
@FeignClient(name = "product-client", url = "http://localhost:8081/product/", configuration = FeignConfig.class)
|
||||||
|
public interface ProductClient {
|
||||||
|
|
||||||
|
@RequestMapping(value = "{id}", method = RequestMethod.GET)
|
||||||
|
Product getProduct(@PathVariable(value = "id") String id);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.defaulterrorhandling.config;
|
||||||
|
|
||||||
|
import feign.Logger;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
public class FeignConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Logger.Level feignLoggerLevel() {
|
||||||
|
return Logger.Level.FULL;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.defaulterrorhandling.controller;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.client.ProductClient;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.model.Product;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController("product_controller1")
|
||||||
|
@RequestMapping(value ="myapp1")
|
||||||
|
public class ProductController {
|
||||||
|
|
||||||
|
private final ProductClient productClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ProductController(ProductClient productClient) {
|
||||||
|
this.productClient = productClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/product/{id}")
|
||||||
|
public Product getProduct(@PathVariable String id) {
|
||||||
|
return productClient.getProduct(id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.defaulterrorhandling.model;
|
||||||
|
|
||||||
|
public class Product {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
private double price;
|
||||||
|
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProductName() {
|
||||||
|
return productName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.client;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.exception.ProductNotFoundException;
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.exception.ProductServiceNotAvailableException;
|
||||||
|
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest
|
||||||
|
public class ProductClientUnitTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductClient productClient;
|
||||||
|
|
||||||
|
private WireMockServer wireMockServer;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void startWireMockServer() {
|
||||||
|
wireMockServer = new WireMockServer(8081);
|
||||||
|
configureFor("localhost", 8081);
|
||||||
|
wireMockServer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void stopWireMockServer() {
|
||||||
|
wireMockServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductApiIsNotAvailable_whenGetProductCalled_thenThrowProductServiceNotAvailableException() {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse().withStatus(503)));
|
||||||
|
|
||||||
|
assertThrows(ProductServiceNotAvailableException.class, () -> productClient.getProduct(productId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductNotFound_whenGetProductCalled_thenThrowBadRequestException() {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse().withStatus(404)));
|
||||||
|
|
||||||
|
assertThrows(ProductNotFoundException.class, () -> productClient.getProduct(productId));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.customizederrorhandling.controller;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.client.ProductClient;
|
||||||
|
import com.baeldung.cloud.openfeign.customizederrorhandling.exception.ErrorResponse;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||||
|
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@WebMvcTest(ProductController.class)
|
||||||
|
@ImportAutoConfiguration({FeignAutoConfiguration.class})
|
||||||
|
public class ProductControllerUnitTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductClient productClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private WireMockServer wireMockServer;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void startWireMockServer() {
|
||||||
|
wireMockServer = new WireMockServer(8081);
|
||||||
|
configureFor("localhost", 8081);
|
||||||
|
wireMockServer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void stopWireMockServer() {
|
||||||
|
wireMockServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductApiIsNotAvailable_whenGetProductCalled_ThenReturnInternalServerError() throws Exception {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(WireMock.get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withStatus(HttpStatus.SERVICE_UNAVAILABLE.value())));
|
||||||
|
|
||||||
|
ErrorResponse expectedError = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
"Product Api is unavailable","uri=/myapp2/product/" + productId);
|
||||||
|
|
||||||
|
MvcResult result = mockMvc.perform(get("/myapp2/product/" + productId))
|
||||||
|
.andExpect(status().isInternalServerError()).andReturn();
|
||||||
|
|
||||||
|
ErrorResponse errorResponse = objectMapper.readValue(result.getResponse().getContentAsString(), ErrorResponse.class);
|
||||||
|
|
||||||
|
assertEquals(expectedError.getCode(), errorResponse.getCode());
|
||||||
|
assertEquals(expectedError.getMessage(), errorResponse.getMessage());
|
||||||
|
assertEquals(expectedError.getStatus(), errorResponse.getStatus());
|
||||||
|
assertEquals(expectedError.getDetails(), errorResponse.getDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductIsNotFound_whenGetProductCalled_ThenReturnInternalServerError() throws Exception {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(WireMock.get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withStatus(HttpStatus.NOT_FOUND.value())));
|
||||||
|
|
||||||
|
ErrorResponse expectedError = new ErrorResponse(HttpStatus.NOT_FOUND,
|
||||||
|
"Product not found","uri=/myapp2/product/" + productId);
|
||||||
|
|
||||||
|
MvcResult result = mockMvc.perform(get("/myapp2/product/" + productId))
|
||||||
|
.andExpect(status().isNotFound()).andReturn();
|
||||||
|
|
||||||
|
ErrorResponse errorResponse = objectMapper.readValue(result.getResponse().getContentAsString(), ErrorResponse.class);
|
||||||
|
|
||||||
|
assertEquals(expectedError.getCode(), errorResponse.getCode());
|
||||||
|
assertEquals(expectedError.getMessage(), errorResponse.getMessage());
|
||||||
|
assertEquals(expectedError.getStatus(), errorResponse.getStatus());
|
||||||
|
assertEquals(expectedError.getDetails(), errorResponse.getDetails());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.defaulterrorhandling.client;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.model.Product;
|
||||||
|
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||||
|
import feign.FeignException;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest
|
||||||
|
public class ProductClientUnitTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductClient productClient;
|
||||||
|
|
||||||
|
private WireMockServer wireMockServer;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void startWireMockServer() {
|
||||||
|
wireMockServer = new WireMockServer(8081);
|
||||||
|
configureFor("localhost", 8081);
|
||||||
|
wireMockServer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void stopWireMockServer() {
|
||||||
|
wireMockServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductIsAvailable_whenGetProductCalled_thenReturnMatchingProduct() {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
String productResponse = "{ " +
|
||||||
|
" \"id\":\"test\",\n" +
|
||||||
|
" \"productName\":\"Watermelon\",\n" +
|
||||||
|
" \"price\":12\n" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
stubFor(get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withStatus(HttpStatus.OK.value())
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody(productResponse)));
|
||||||
|
|
||||||
|
Product product = productClient.getProduct(productId);
|
||||||
|
|
||||||
|
assertEquals(productId, product.getId());
|
||||||
|
assertEquals("Watermelon", product.getProductName());
|
||||||
|
assertEquals(12.00d, product.getPrice(), 0.00d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductApiIsNotAvailable_whenGetProductCalled_thenThrowFeignException() {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withStatus(HttpStatus.SERVICE_UNAVAILABLE.value())));
|
||||||
|
|
||||||
|
assertThrows(FeignException.class, () -> productClient.getProduct(productId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductIdNotFound_whenGetProductCalled_thenThrowFeignException() {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withStatus(HttpStatus.NOT_FOUND.value())));
|
||||||
|
|
||||||
|
assertThrows(FeignException.class, () -> productClient.getProduct(productId));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.defaulterrorhandling.controller;
|
||||||
|
|
||||||
|
import com.baeldung.cloud.openfeign.defaulterrorhandling.client.ProductClient;
|
||||||
|
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||||
|
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@WebMvcTest(ProductController.class)
|
||||||
|
@ImportAutoConfiguration({FeignAutoConfiguration.class, TestControllerAdvice.class})
|
||||||
|
@EnableWebMvc
|
||||||
|
public class ProductControllerUnitTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductClient productClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
private WireMockServer wireMockServer;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void startWireMockServer() {
|
||||||
|
wireMockServer = new WireMockServer(8081);
|
||||||
|
configureFor("localhost", 8081);
|
||||||
|
wireMockServer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void stopWireMockServer() {
|
||||||
|
wireMockServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductServiceIsnotAvailable_whenGetProductCalled_thenReturnInternalServerError() throws Exception {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(WireMock.get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse().withStatus(HttpStatus.SERVICE_UNAVAILABLE.value())));
|
||||||
|
|
||||||
|
mockMvc.perform(get("/myapp1/product/" + productId))
|
||||||
|
.andExpect(status().is(HttpStatus.INTERNAL_SERVER_ERROR.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenProductIsNotFound_whenGetProductCalled_thenReturnBadeRequestError() throws Exception {
|
||||||
|
String productId = "test";
|
||||||
|
|
||||||
|
stubFor(WireMock.get(urlEqualTo("/product/" + productId))
|
||||||
|
.willReturn(aResponse().withStatus(HttpStatus.NOT_FOUND.value())));
|
||||||
|
|
||||||
|
mockMvc.perform(get("/myapp1/product/" +productId))
|
||||||
|
.andExpect(status().is(HttpStatus.INTERNAL_SERVER_ERROR.value()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.baeldung.cloud.openfeign.defaulterrorhandling.controller;
|
||||||
|
|
||||||
|
import feign.FeignException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class TestControllerAdvice {
|
||||||
|
|
||||||
|
@ExceptionHandler({FeignException.class})
|
||||||
|
public ResponseEntity<Object> handleFeignException(FeignException exception) {
|
||||||
|
return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user