diff --git a/spring-cloud-modules/spring-cloud-openfeign/pom.xml b/spring-cloud-modules/spring-cloud-openfeign/pom.xml
index a6a24c376e..0aa954691c 100644
--- a/spring-cloud-modules/spring-cloud-openfeign/pom.xml
+++ b/spring-cloud-modules/spring-cloud-openfeign/pom.xml
@@ -61,6 +61,12 @@
spring-boot-starter-test
test
+
+ com.github.tomakehurst
+ wiremock-jre8
+ 2.33.2
+ test
+
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/client/ProductClient.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/client/ProductClient.java
new file mode 100644
index 0000000000..8a6c5d5846
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/client/ProductClient.java
@@ -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);
+
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/config/CustomErrorDecoder.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/config/CustomErrorDecoder.java
new file mode 100644
index 0000000000..3750e6288d
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/config/CustomErrorDecoder.java
@@ -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");
+ }
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/config/FeignConfig.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/config/FeignConfig.java
new file mode 100644
index 0000000000..5cddd521f1
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/config/FeignConfig.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/controller/ProductController.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/controller/ProductController.java
new file mode 100644
index 0000000000..9b7a5aea1d
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/controller/ProductController.java
@@ -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);
+ }
+
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ErrorResponse.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ErrorResponse.java
new file mode 100644
index 0000000000..c72e3db68b
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ErrorResponse.java
@@ -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;
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductExceptionHandler.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductExceptionHandler.java
new file mode 100644
index 0000000000..2ed709bc34
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductExceptionHandler.java
@@ -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 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 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 handleGenericException(Exception exception, WebRequest request) {
+ return new ResponseEntity<>(new ErrorResponse(
+ HttpStatus.INTERNAL_SERVER_ERROR,
+ exception.getMessage(),
+ request.getDescription(false)),
+ HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductNotFoundException.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductNotFoundException.java
new file mode 100644
index 0000000000..337cb89f7b
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductNotFoundException.java
@@ -0,0 +1,8 @@
+package com.baeldung.cloud.openfeign.customizederrorhandling.exception;
+
+public class ProductNotFoundException extends RuntimeException {
+
+ public ProductNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductServiceNotAvailableException.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductServiceNotAvailableException.java
new file mode 100644
index 0000000000..ce30f8c310
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/customizederrorhandling/exception/ProductServiceNotAvailableException.java
@@ -0,0 +1,8 @@
+package com.baeldung.cloud.openfeign.customizederrorhandling.exception;
+
+public class ProductServiceNotAvailableException extends RuntimeException {
+
+ public ProductServiceNotAvailableException(String message) {
+ super(message);
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/client/ProductClient.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/client/ProductClient.java
new file mode 100644
index 0000000000..b842b948d7
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/client/ProductClient.java
@@ -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);
+
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/config/FeignConfig.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/config/FeignConfig.java
new file mode 100644
index 0000000000..f2329ebe0b
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/config/FeignConfig.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/ProductController.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/ProductController.java
new file mode 100644
index 0000000000..df352f8d52
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/ProductController.java
@@ -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);
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/model/Product.java b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/model/Product.java
new file mode 100644
index 0000000000..25a1662c99
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/defaulterrorhandling/model/Product.java
@@ -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;
+ }
+
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/customizederrorhandling/client/ProductClientUnitTest.java b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/customizederrorhandling/client/ProductClientUnitTest.java
new file mode 100644
index 0000000000..7cf2a12692
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/customizederrorhandling/client/ProductClientUnitTest.java
@@ -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));
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/customizederrorhandling/controller/ProductControllerUnitTest.java b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/customizederrorhandling/controller/ProductControllerUnitTest.java
new file mode 100644
index 0000000000..cc9d029e07
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/customizederrorhandling/controller/ProductControllerUnitTest.java
@@ -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());
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/client/ProductClientUnitTest.java b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/client/ProductClientUnitTest.java
new file mode 100644
index 0000000000..392cb19050
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/client/ProductClientUnitTest.java
@@ -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));
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/ProductControllerUnitTest.java b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/ProductControllerUnitTest.java
new file mode 100644
index 0000000000..269cd2d2d6
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/ProductControllerUnitTest.java
@@ -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()));
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/TestControllerAdvice.java b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/TestControllerAdvice.java
new file mode 100644
index 0000000000..0c7ed86b7c
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/defaulterrorhandling/controller/TestControllerAdvice.java
@@ -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