How to test a RestClientException with MockRestServiceServer - java

While testing a RestClient-Implementation I want to simulate a RestClientException that may be thrown by some RestTemplate-methods in that implementation f.e. the delete-method:
#Override
public ResponseEntity<MyResponseModel> documentDelete(String id) {
template.setErrorHandler(new MyResponseErrorHandler());
ResponseEntity<MyResponseModel> response = null;
try {
String url = baseUrl + "/document/id/{id}";
response = template.exchange(url, DELETE, null, MyResponseModel.class, id);
} catch (RestClientException ex) {
return handleException(ex);
}
return response;
}
How can I achieve this?
I define the mock-server in this way:
#Before
public void setUp() {
mockServer = MockRestServiceServer.createServer(template);
client = new MyRestClient(template, serverUrl + ":" + serverPort);
}

You can test throwing runtime exceptions from the MockRestServiceServer, although this class, as of Spring 5.0.0.RC4, is not designed for it (which means it may not work for more complex use cases):
RestTemplate yourApi;
MockRestServiceServer server = MockRestServiceServer.createServer(yourApi);
server.expect(requestTo("http://..."))
.andRespond((response) -> { throw new ResourceAccessException(
new ConnectException("Connection reset")); });
It seems to work in tests:
where there's only one RestTemplate call,
where the exception is thrown as a result of the last expectation.
I wasn't able to expect two consecutive exceptions; the MockRestSeriviceServer (more concrete, the SimpleRequestExpectationManager) throws IllegalStateException on replaying the second expectation.

You can take advantage of the MockRestResponseCreators for mocking 4xx or 5xx responses from the mockRestServiceServer.
For example for testing a 5xx - Internal server error:
mockServer.expect(requestTo("your.url"))
.andExpect(method(HttpMethod.GET/POST....))
.andRespond(withServerError()...);
In your case the RestClientException is thrown for client-side HTTP errors, so
the example above can be fine tuned for a 4xx exception by using:
...andRespond(withBadRequest()); or ...andRespond(withStatus(HttpStatus.NOT_FOUND));
For a more simpler usage of these methods you use static imports for org.springframework.test.web.client.MockRestServiceServer,org.springframework.test.web.client.response.MockRestResponseCreators

Answer by Alex Ciocan works for different http status responses, so if you want those, go with that as that's the cleanest way to go. I had a problem that I needed to be able to test also for connection reset and other network-level problems, which are trickier to simulate.
Answer by MaDa works for some use cases, but it didn't work for me when using AsyncRestTemplate, as it throws too early. However it did lead me to right direction. This one seems to work with async calls as well:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
// ...
ClientHttpResponse exceptionThrowingResponse = mock(ClientHttpResponse.class);
when(exceptionThrowingResponse.getStatusCode()) // getRawStatusCode() in latest spring
.thenThrow(new IOException("connection reset"));
mockServer.expect(requestTo("http://localhost:123/callme"))
.andRespond((response) -> exceptionThrowingResponse);
This seems to also work for consecutive exceptions, as well as different http statuses.

How about this :
#Spy
#InjectMocks
ClasstoMock objToMock;
#Test
public void testRestClientException() throws Exception {
try {
Mockito.when(this.objToMock.perform()).thenThrow(new RestClientException("Rest Client Exception"));
this.objToMock.perform();
}
catch(Exception e){
Assert.assertEquals(RestClientException.class, e.getClass());
}

Related

Flux consumer doesnt stop to consume data

I have this implementation
#Override
public Flux<byte[]> translateData(final String datasetId) {
return keyVaultRepository.findByDatasetId(datasetId)
.map(keyVault -> {
try {
return translatorService.createTranslator(keyVault.getKey()); // throws CryptoException in the test
} catch (CryptoException e) {
throw Exceptions.propagate(new ApiException("Unable to provide translated file"));
}
})
.flatMapMany(translator -> storageService.getEntry(datasetId).map(translator::update));
}
and this failing test
#Test
void getTranslatedDataWithError() throws StorageException, CryptoException {
final List<byte[]> bytes = new ArrayList<>();
// exec + validate
getWebTestClient()
.get()
.uri(uriBuilder -> uriBuilder.path("/{datasetId}").build(datasetId))
.exchange()
.expectStatus().is5xxServerError()
.returnResult(byte[].class)
.getResponseBody()
.onErrorStop()
.subscribe(bytes::add);
assertThat(bytes).isEmpty();
}
the part .is5xxServerError() is succeeding but the list is not empty.
The Microservice which is calling the endpoint of translateData should not consume any data from upstream but apparently this is the case.
I've found a workaround by throwing a RuntimeException in the catch block (I could also make the CryptoException unchecked but thats not the matter of my question) and then handle the case in my ControllerAdvice/GlobalExceptionhandler and just return a ResponseEntity with an ErrorDto .
The core of my question is, how can I do this natively with Flux. So that the consumer of the endpoint notices there is an error and .subscribe(bytes::add); wont be even executed.
I have tried it already with .doOnError , .onErrorResume etc. but it always ends with a non empty list :(
By this I am afraid that the bytes will later delivered to the client (which should not of course he should get an error response)

JUnit5 Test coverage for catch block of a Spring Boot Controller

I am trying to write test for my controller with the code below. I want to cover the test for the code in the catch block statement but I'm not able to write one. I want to return a Server Response with failure code and message in the catch block.
#PostMapping(COUNTERS)
public ResponseEntity<?> getCounters(#Valid #RequestBody ApartmentCounterListRequest requestData) {
try {
log.debug("Entering API for counter list");
ApartmentCounterListResponse apartmentCounterListResponse = counterService.getAllCounters();
return ResponseEntity.ok(apartmentCounterListResponse);
} catch (Exception exception) {
log.error("Exception in counter list :: ", exception);
ServerResponse serverResponse = ResponseBuilder.buildVendorFailureMessage(new ServerResponse(),
RequestResponseCode.EXCEPTION);
return ResponseEntity.ok(JsonResponseBuilder.enquiryResponse(serverResponse));
}
}
My test code is as follows:
#Test
#DisplayName("Should return ServerResponse with failure data.")
void Should_Return_Server_Response_On_Exception() throws Exception {
/*given*/
ApartmentCounterListRequest apartmentCounterListRequest = ApartmentTestUtil.getApartmentCounterListRequest.
apply("test", "test");
Mockito.when(counterServic.getAllCounters()).thenThrow(new Exception());
// ServerResponse serverResponse = ApartmentTestUtil.getServerExceptionServerResponse.get();
/*then*/
mockMvc.perform(
post(COUNTER_URL)
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(apartmentCounterListRequest)))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.resultCode", Matchers.is("-6")));
verify(counterService, times(1)).getAllCounters();
}
When I run this test I am getting the following error:
org.mockito.exceptions.base.MockitoException:
Checked exception is invalid for this method!
Invalid: java.lang.Exception
I have gone through some of the following posts but haven't found a suitable answer yet.
Unit testing code in catch block of a Spring Controller
Java - How to Test Catch Block?
Unit testing code in catch block of a Spring Controller
JUnit for both try and catch block coverage
Can anyone help me write test that covers the catch block or tell me the way to do it?
I have this try catch in my controller to handle any unexpected exceptions. And for different api's I have to send a response with different response code and messages which doesnot allow me to use Exception handler.
The method you are mocking does not declare a checked exception, therefore Mockito is not able to throw one from there. Try to have the mock throw an unchecked exception (i.e. RuntimeException).
You can try to use willAnswer
Mockito.when(counterServic.getAllCounters()).thenAnswer(x -> {throw new Exception() });
Maybe this is a bit misused as Answer is used for more complex for when return

issue with mockito test for try catch block with slif4j Log

I am writting test for a try catch block, but I am quite confused about how to test the catch block...especially it uses slf4j to log the error.
addText here is the another method from the same class.
public class TextQueue {
public void addTextToQueue(final Text text) {
try {
if (text != null) {
addText(text);
}
} catch (final JsonProcessingException e) {
LOGGER.error("Error adding text to the queue : {}", e);
}
}
}
here is my test case
#RunWith(MockitoJUnitRunner.class)
public class TextQueueTest {
private org.slf4j.Logger LOGGER = LoggerFactory.getLogger(TextQueueTest.class);
private static final String MY_TEXT = "src/text.json";
private Text text;
private final ObjectMapper mapper = new JacksonConfig().dateAsStringObjectMapper();
#Mock
private TextQueue textQueue;
#Before
public void setUp() throws IOException {
text = mapper.readValue(new File(TextQueueTest.MY_TEXT), Text.class);
}
#Test
public void addTextToQueue() {
try{
textQueue = spy(textQueue);
textQueue.addTextToQueue(text);
}catch(final Exception e){
LOOGER.error("add text to queue threw an error" + e);
}
}
can anyone help me solve this problem?
First of all, you should really read a good tutorial about Mockito, like the one from vogella. You see, you are simply throwing together a lot of things that are non-sensical.
Like:
#Mock
private TextQueue textQueue;
to then have
textQueue = spy(textQueue);
within your test case. You should be really clear about this. A spy is build on a real instance of your class under test. Creating a spy that spies on a mock, as said: that makes no sense.
Then:
}catch(final Exception e){
Logger.error("add text to queue threw an error" + e);
Again, non-sensical. The whole idea of your unit tests is that they fail when something is wrong. When your production code throws unexpected exceptions, you don't log them, you just let them fail your test case in the end.
To answer the actual question: it looks like your production code is using a specific "constant" logger instance. Given that design, the only way to check your production code would be to:
make that LOGGER a mocked object
somehow inject it into an instance underTest of your production code class
trigger that method to test on underTest (and somehow force the method to throw an exception)
verify that the mocked LOGGER saw the expected call to error()
We can't give better advise, because your code input isn't sufficient, we don't really know what your production class is doing (for example: we don't know what LOGGER is, and where it is coming from. if it happens to be a static variable, then most likely, you can't get "control" over it with Mockito).
In any case, you probably actually need the spy concept. In order to test addTextToQueue() you need a way to invoke the "real" addTextToQueue() implementation, but the call to addTser() within needs to go to a mock (so that you can control what that call does).
But as said: start by really researching how Mockito works, instead of throwing together things that make no sense in some "trial and error" approach. Correct unit testing with mocking is complicated, you can't learn that by "trial and error".

How to return http status code for exceptions in rest services

In my application I have different layers like the rest layer, service layer and DB layer, according to business scenarios I am trowing different business exceptions from the service layer.
But now, I have to set different HTTP codes like 400, 403, 409, 412.. to REST responses.
How can I set different HTTP status codes based on different scenarios?
Which is the most feasible way like: aspect, exception mapper, or ....?
Since I can set HTTP status only once in rest layer (
referred this ), I am not able to map to different HTTP codes because my exception is from service layer.
My exception class looks like this:
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BusinessException(ErrorEnumeration error) {
}
public BusinessException(Exception e, ErrorEnumeration error) {
}
}
and the exception will be thrown from the service like this:
throw new BusinessException(ErrorEnumeration.VALIDATION_FAILED);
Please help by suggesting a solution
You can use exceptions defined in jax-rs or you can use your own exceptions. Fist catch your business exceptions and convert them to jax-rs versions. For example, for 404 you can throw javax.ws.rs.NotFoundException.
You can also write your own exceptions by extending them from javax.ws.rs.ClientErrorException
Here is an example for 409-Conflict status exception
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.core.Response;
public class ConflictException extends ClientErrorException{
public ConflictException(Response.Status status) {
super(Response.Status.CONFLICT); // 409
}
}
Update
Most simple and feasible way is catching your business exceptions and re-throw them with jax-rs exceptions.
try{
businessService.executeBusinessRule();
}catch (BusinessException e){
// It is better if your BusinessException has some child class to handle
if(e.getError() == ErrorEnumeration.VALIDATION_FAILED){
throw new BadRequestException();
}else{
throw new ConflictException();
}
}
If you are using spring you can always catch these exceptions using aop.
#Aspect
public class BusinessExceptionInterceptor{
#AfterThrowing(pointcut = "execution(* com.your.service.packge..* (..))", throwing = "e")
public void errorInterceptor(BusinessException e) {
// re-throw again...
}
Update 2
Also it is better to define a new exception instead of reusing same exception with different state. You can define a new ValidationException which extends from BusinessException like this.
public class ValidationException extends BusinessException{
public ValidationException() {
super(ErrorEnumeration.VALIDATION_FAILED);
}
}
By using this way you can still handle all the BusinessException but it is easier to identify or map them to Jax-rs exceptions.

Is this a bad practice?

Is the following code considered a bad practice? Do you think it can be done otherwise?
The goal is to always update the status, either with success (i.e invocation to service.invoke(id);returns normally ) or with failure...
#Autowired
private Service service;
public void onMessage(Message message) {
String id = null;
String status = "FAILED";
try {
id = ((TextMessage) message).getText();
status = service.invoke(id); //can throw unchecked exception
} catch (final JMSException e) {
throw new RuntimeException(e);
} finally {
if (StringUtils.isNumeric(id)) {
service.update(id, status);
}
}
}
It depends on your Use-case, whether you have to perform a step or not based on previous step. Using finally may execute your second step regardless what exception you may receive.
I would recommend having the second step outside try...catch block so that you'll update only when you have got any exception you've Expected and continue to your second step, else, your method will throw and exit.
i think you should not use implementation of message listener , you should wire them independent of spring tech . just pojo based . use <jms:listener-container > with <jms:listener>

Categories

Resources