I'm trying to write a unit test for resttemplate.getForEntity() method and trying to cover Exception.class catch block.
Below is the code for same:
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.client.RestTemplate;
@Service
@Slf4j
public class ProductService {
private String productInfoBaseUrl = "http://localhost";
@Value("${service.product.metadata}")
private String productInfoPathProductMetadata;
private static final Logger logger = LoggerFactory.getLogger(ProductService.class);
@Autowired
RestTemplate restTemplate;
@Retryable(retryFor = {RuntimeException.class}, maxAttempts = 3, backoff = @Backoff(delay = 10000))
public ProductResponse getProductMetadata(String sku, HandlerResult handlerResult) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "application/json");
String errorMsg;
String endpoint = getProductFetchMetadataEndpoint();
ResponseEntity<String> response;
try {
response = restTemplate.getForEntity(endpoint, String.class);
} catch (RuntimeException ex) {
errorMsg = "getProductMetadata threw exception: " + ex.getMessage();
HandlerResult.updateStatus(handlerResult, errorMsg, false);
throw ex;
}
catch (Exception ex) {
errorMsg = "getProductMetadata threw exception: " + ex.getMessage();
log.info(errorMsg);
HandlerResult.updateStatus(handlerResult, errorMsg, false);
return null;
}
ProductResponse metadataResponse;
Gson gson = new Gson();
try {
metadataResponse = gson.fromJson(response.getBody(), ProductResponse.class);
} catch (Exception e) {
errorMsg = String.format("Exception ProductResponse converting %s to map", response.getBody());
log.info(errorMsg);
HandlerResult.updateStatus(handlerResult, errorMsg, false);
return null;
}
return metadataResponse;
}
private String getProductFetchMetadataEndpoint() {
return productInfoBaseUrl + "/" + productInfoPathProductMetadata + "/";
}
}
Below is the Unit Test Code:
@ExtendWith(MockitoExtension.class)
public class ProductServiceTest {
@InjectMocks
ProductService productService;
//@Mock
@Spy
RestTemplate restTemplate;
@Test
public void testGetProductMetadataException() throws Exception {
Mockito.doThrow(RestClientException.class).when(restTemplate).getForEntity(any(), any());
ProductMetadataResponse result = productService.getProductMetadata("NX35100", null);
Assertions.assertNull(result);
}
}
I'm getting below error by running above unit test:
org.mockito.exceptions.base.MockitoException:
Checked exception is invalid for this method!
Invalid: java.lang.Exception
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:419)
Can someone please guide me on what I'm missing?
I think there are 2 problems here:
You should use
@Mockinstead of@Spyas@Spywill just monitor the actual instance and the actual method will still be triggered when the method is invoked. In your use case, you want the exception to be thrown whengetForEntitymethod is invoked.You should use
when(xxx).thenThrow(yyy), notdoThrow(yyy).when(xxx).getForEntity(...). The latter is designed forvoidreturn type. AndgetForEntityis notvoidmethod.