Blog
This blog post gives you an effortless way to test whether your exception handling of Spring Boot's RestTemplate is working. In order to do so we leverage Chaos Engineering and this way can prevent the unattractive path of mocking a part of your system or cumbersome manual testing effort.
Spring Boot’s RestTemplate
Microservices and distributed applications are a rising trend in today’s software development practices. One thing for sure is that at some point two microservices have the need to communicate with each other. The simplest approach to achieve that is to use HTTP-based communication like REST (Representational State Transfer). To do that, we use in Spring Boot the RestTemplate as shown in the Listing below. In this example we reach an endpoint given as parameter (url) via HTTP GET and expect a list of type Product in the response body. This response is used in an orchestrated endpoint reachable via /products/ and collects various products from endpoints of different microservices (more context of the demo online shop can be found here).
@RestController
@RequestMapping("/products")
public class ProductsController {
@Value("${rest.endpoint.hotdeals}")
private String urlHotDeals;
...
@GetMapping
public Products getProducts() {
Products products = new Products();
products.setFashion(getProduct(urlFashion));
products.setToys(getProduct(urlToys));
products.setHotDeals(getProduct(urlHotDeals));
return products;
}
private List<Product> getProduct(String url) {
return restTemplate.exchange(
url,
HttpMethod.GET,
null,
productListTypeReference
).getBody();
}
}
Well, obviously this code is very simplified and in reality we also need to think about the error case. What if the other system is not available? Or answers slowly? What if the response is different than expected? Or erroneous? How to verify that without changing the other system’s source code? Right now, the code will throw an (unhandled) exception. To be specific, a HttpClientErrorException in case of HTTP 4xx responses and a HttpServerErrorException in case of HTTP 5xx responses. In order to handle the exception appropriately Spring Boot offers multiple ways - as shown e.g. in the blog post of Baeldung about "Spring RestTemplate Error Handling".
Tests first, keep Code as it is
So, while the above described solution works, we haven’t considered the error handling for now. Before improving that we follow the paradigm of Test Driven Development of tests-first and would like to test it for real - without mocking or temporary code changes. Luckily, steadybit is able to change the behavior of a Java application at runtime via bytecode injection, without re-deploying anything and without the requirement of additional source code dependencies. Once the agents are installed, steadybit has covered the application as visible on our dashboard (see Figure below) and we can start with our first tests on unhandled exceptions.
As steadybit has already discovered our system we can easily create a few experiments to check on the behavior. We start with testing what happens if one of the called endpoints (e.g. hotdeals at ${urlHotDeals}/products) throws an exception.
Step 1: Create Application Experiment
The first thing to do is to create and define a new experiment to provoke an erroneous endpoint. When using steadybit, this is pretty straightforward:
1. We go to Experiments, choose to create a new Application Experiment (because we are injecting chaos on the level of the Java Virtual Machine).
2. We give the experiment a useful name (e.g. "Gateway is able to handle Hot-Deals exceptions") and choose the Global area for now.
3. We choose to attack the hotdeals application as visible below.

1. Since we want to have a maximum effect we choose to have an impact of 100% at the following wizard step.
2. We apply the "controller exception" attack at the next step to provoke an exception at GET-requests on the /products-endpoint of hotdeals (see Figure below).

Finally, we can use the HTTP Status check of the monitoring section to verify that the Gateway`s endpoint of http://k8s.demo.steadybit.io/products always responds with a HTTP OK status within a timeout of 3 seconds (see Figure below). This is our desired behavior of always having products at the gateway even when one of the product-microservices is failing. Create the experiment by finalising the wizard via "save".

Step 2: Run Experiment to Check Behavior
Now it’s time to verify the status quo. By starting the experiment we can see what happens without the need to change anything in the source code or shutting down parts of the system (in this case hotdeals).

Well, okay, that was expected. The exception injected by steadybit in Hot-Deals lead to an erroneous response of the RestController-endpoint (see logs below). This affects Gateway`s current implementation by also returning an HTTP 500 (see Figure above). So, by injecting an exception at component 1 (Hot-Deals) and stopping it to work, we also caused an exception at the dependend component 2 (Gateway).
2021-06-15 17:35:19.389 ERROR Request processing failed; nested exception is: java.lang.RuntimeException: Exception injected by steadybit at com.steadybit.HotDealsRestController.getHotDeals(HotDealsRestController.java:28)
...
021-06-15 17:35:55.891 ERROR 500 Server Error for HTTP GET "/products" org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 : [{"timestamp":"2021-06-15T17:35:55.888+00:00","status":500,... (5526 bytes)]
...
Improve Implementation
Our goal is now to fix the current implementation by adding proper exception handling and afterwards validate it via the created experiment. There are two general places to work on the issue: First one is to fix the code at Hot-Deals being able to provide a fallback instead of an HTTP 500, the second one is to fix the code at Gateway being able to work with erroneous product-responses and provide a proper fallback. We decide to fix it in the Gateway by applying the simplest solution possible: a try-catch-block as seen in Listing below.
private List<Product> getProduct(String url) {
try {
return restTemplate.exchange(
url,
HttpMethod.GET,
null,
productListTypeReference
).getBody();
} catch (RestClientException e) {
log.error("RestClientException while fetching products", e);
return Collections.emptyList();
}
}
Validate Improvement
Now that we have improved the implementation we definitely need to check whether it works as expected. For that we simply reuse the existing experiment and re-execute it. This time the experiment is successful since the /product-endpoint always returns a HTTP OK with a valid JSON. In case hotdeals is erroneous it’s content is simply replaced by an empty list.

Conclusion
In this blog post we have tested and also improved the error handling for a synchronous HTTP request. By using steadybit we were able to verify the different error states without changing any source code or shutting down parts of the system. However, the current implementation is still very simplistic. In a real world use case you may need a more advanced solution as for instance a circuit breaker. Thereby, the gateway-component is reducing the amount of requests forwarded to hotdeals and reduces the load on hotdeals. This is especially desirable when the reason of hotdeals' exception is related to increased load. Checkout the implementation of that endpoint in GitHub and verify it with the created experiment as well.
Are we safe now?
The short answer to this question is: no. There is definitely more to test and we will cover these in future blog posts. Stay tuned!