Mock amazon web service Request/Response object in junit - java

I am working in AWS(Amazon web service) , mockito and java junit4 environment. In my class I am using one method which takes Request object as a parameter and depending on that object I am getting response. Following is my code,
private Response<String> getStringResponse(Request<?> request) {
try {
AmazonHttpClient client = new AmazonHttpClient(new ClientConfiguration());
ExecutionContext executionContext = new ExecutionContext(true);
HttpResponseHandler<AmazonClientException> handler = getErrorResponseHandler();
HttpResponseHandler<String> responseHandler = getHttpResponseHandler();
RequestExecutionBuilder requestExecutionBuilder = client.requestExecutionBuilder();
requestExecutionBuilder = requestExecutionBuilder.executionContext(executionContext);
requestExecutionBuilder = requestExecutionBuilder.request(request);
requestExecutionBuilder = requestExecutionBuilder.errorResponseHandler(handler);
Response<String> response = requestExecutionBuilder.execute(responseHandler);
return response;
} catch (Exception e) {
AppLogger.getLogger().error("Exception in :: classname :: getStringResponse() ::");
throw e;
}
}
What I want to do is, I want to mock this whole scenario means on whatever request, my method should give me the custom response object which I want, irrespective of what Request is coming. I am calling this method from my junit test. So is there anyway to do this?

Before you can test this method with JUnit and Mockito,
you should write a clean and testable codes.
Importantly, you have to remove all dependencies inside method and initialize them from outside. For example,
private Response<String> getStringResponse(Request<?> request,
AmazonHttpClient client,
ExecutionContext executionContext,
HttpResponseHandler<AmazonClientException> handler,
HttpResponseHandler<String> responseHandler) {
try {
RequestExecutionBuilder requestExecutionBuilder = client.requestExecutionBuilder()
.executionContext(executionContext)
.request(request)
.errorResponseHandler(handler);
Response<String> response = requestExecutionBuilder.execute(responseHandler);
return response;
} catch (Exception e) {
AppLogger.getLogger().error("Exception in :: classname :: getStringResponse() ::");
throw e;
}
}
Now you can test it by mocking these above dependencies.
#Mock
AmazonHttpClient client;
#Mock
ExecutionContext executionContext;
#Mock
HttpResponseHandler<AmazonClientException> handler;
#Mock
HttpResponseHandler<String> responseHandler;
// For request, you can create a custom one or use mock data
Request<?> request;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
// other setups
}
#Test
public void getStringResponseTest() {
// you can test now
yourCall.getStringResponse(request, client, executionContext, handler, responseHandler);
// verify whatever you want....
}

Related

Spring Boot Test: Expected request don't persist using ExpectedCount.manyTimes()

I'm trying to run some integration tests on my code and I use a MockRestServiceServer from spring-boot-test in order to set up the expected requests.
I have one call that is called many times while running my test, but it seems not to persist during the test. My test looks like this:
#Test
void getHealthStatus() {
try {
RequestBuilder request = get("/actuator/hc").contentType("application/json");
MockServerBinder.bindPersistentThingworxPropertiesCall(
mockServer,
requestTo(new URI(String.format("%sProperties/TestProp", thingworxUrl))),
objectMapper.writeValueAsString(new PingResponse(DashboardIndicator.HEALTHY, 200))
);
DashboardStatusModel expectedResult = new DashboardStatusModel();
expectedResult.addResult("spring",service.getAppHealth());
expectedResult.addResult("thingworx", service.getThingworxAvailability());
assertOpenUrl(request);
MvcResult result = mockMvc.perform(get("/actuator/hc").contentType("application/json"))
.andExpect(status().isOk())
.andReturn();
DashboardStatusModel actualResult = objectMapper.readValue(result.getResponse().getContentAsString(), DashboardStatusModel.class);
assertEquals(expectedResult.getResults().get("spring"), actualResult.getResults().get("spring"));
assertEquals(expectedResult.getResults().get("thingworx").getStatus(),actualResult.getResults().get("thingworx").getStatus());
assertEquals(expectedResult.getResults().get("thingworx").getData().get("url"), actualResult.getResults().get("thingworx").getData().get("url"));
} catch (Exception e) {
fail("Unable to perform REST call on GDP-API", e);
}
}
As additional information:
mockServer is created in a superclass like this:
protected static MockRestServiceServer mockServer;
#BeforeEach
public void configureMockServer() {
mockServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
}
MockServerBinder.bindPersistentThingworxPropertiesCall() is a helper class that looks like this:
public static void bindPersistentThingworxPropertiesCall(MockRestServiceServer mockServer, RequestMatcher request, String responseJSONasString){
mockServer.expect(ExpectedCount.once(), request)
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(responseJSONasString));
}
assertOpenUrl(request); is a function that checks if a URL doesn't have any authentication by using a MockMVC:
public void assertOpenUrl(RequestBuilder request){
try{
mockMvc.perform(request).andExpect(status().isOk());
} catch (Exception e) {
fail("Unable to perform REST call on GDP-API", e);
}
}
When I run this test, the expectedResult.addResult("thingworx", service.getThingworxAvailability()); will be able to use the MockRestServiceServer, but the assertOpenUrl(request); line will fail, because MockRestServiceServer doesn't expect anymore calls to the endpoint binded in MockServerBinder.bindPersistantThingworxPropertyCall(). This does not happen if I Copy & Paste MockServerBinder.bindPersistantThingworxPropertyCall() under the existing one, so I think it's a problem with how I binded the request in the first place.
From what I understand ExpectedCount.manyTimes() should keep this request during the test.
Is this not true or is there another way I should bind my request so that it stays available during the entire test?
PEBCAK issue.
As you can see in the bindPersistentThingworxPropertiesCall(), I actually didn't use ExpectedCount.manyTimes(). I didn't catch that. Changed it and now it works.

How to test InternalServerError using mockito in Repository?

I am writing a test to test the POST method for failure case in the controller.
It returns a 415, I am expecting 500. I have mocked the response using mockito.
ControllerTest
#Test
#DisplayName("POST /customers - Failure")
void createProductShouldFail() throws Exception {
// Setup mocked service
when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}");
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
}
Controller
#PostMapping(path = "/customers")
public ResponseEntity<Customer> saveCustomer(#RequestBody Customer customer){
try {
// Create the new product
Customer savedCustomer = customerService.save(customer);
// Build a created response
return ResponseEntity
.created(new URI("/customers/" + savedCustomer.getId()))
.body(savedCustomer);
} catch (URISyntaxException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
Error:
HTTP Method = POST
Request URI = /customers
Parameters = {}
Headers = [Accept:"application/json", Content-Length:"15"]
Body = {"name":"John"}
Session Attrs = {}
Handler:
Type = com.prabhakar.customer.controller.CustomerController
Method = com.prabhakar.customer.controller.CustomerController#saveCustomer(Customer)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 415
Error message = null
Headers = [Accept:"application/json, application/*+json"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
org.opentest4j.AssertionFailedError:
Expected :500
Actual :415
But 415-Unsupported Media Type client error response code.
I have used the same payload for this method,it works.
#Test
#DisplayName("POST /customers - Success")
void createProductShouldSucceed() throws Exception {
// Setup mocked service
Customer mockCustomer = new Customer(1L, "John");
when(customerService.save(any(Customer.class))).thenReturn(mockCustomer);
this.mockMvc.perform(post("/customers")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}"))
// Validate the response code and content type
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
//Validate returned json fields
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.name").value("John"));
}
Update I have added
#RestController
#EnableWebMvc
this throws an error as mocked But the code breaks near mockmvc.perform.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError
How can I verify if this is working.
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
There are two thing you must have in account to solve the problem:
First, Instead of use .accept(MediaType.APPLICATION_JSON) you must use .contentType(MediaType.APPLICATION_JSON).
Second, the other thing you must have in account is, if you are not handling the exception (using a controller advice or other way) you must do it, because when you execute the firts step you will receive the following error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError
The workaround that I took was use #ExceptionHandler in the CustomerController to test your code (this isn't the best place to do this, depending what you are doing. Instead use a #ControllerAdvice. You can find some examples here https://www.baeldung.com/exception-handling-for-rest-with-spring).
Below the complete code that are used to recreate your case.
Customer.class
public class Customer {
private Long id;
private String name;
public Customer(Long id, String name) {
this.id = id;
this.name = name;
}
// + getter and setter
}
CustomerController.class
#RestController
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
#PostMapping(path = "/customers")
public ResponseEntity<Customer> saveCustomer(#RequestBody Customer customer) {
try {
// Create the new product
Customer savedCustomer = customerService.save(customer);
// Build a created response
return ResponseEntity
.created(new URI("/customers/" + savedCustomer.getId()))
.body(savedCustomer);
} catch (URISyntaxException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
// Code used to avoid the error explained in the second step
#ExceptionHandler
public ResponseEntity<?> handlingInternalServerError(HttpServerErrorException.InternalServerError ex) {
// code to be executed when the exception is thrown (logs, ...)
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CustomerService.class
#Service
public class CustomerService {
public Customer save(Customer customer) {
return customer;
}
}
CustomerControllerTest.class
#SpringBootTest
#AutoConfigureMockMvc
class CustomerControllerTest {
#MockBean
private CustomerService customerService;
#Autowired
private MockMvc mockMvc;
#Test
#DisplayName("POST /customers - Failure")
void saveCustomer() throws Exception {
Customer customerMock = new Customer(1L, "John");
// Setup mocked service
when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
RequestBuilder requestBuilder = post("/customers")
.content("{\"name\":\"John\"}")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
}
}
NOTE: This test was executed using Java 8 and JUnit5
Other NOTE based on your comment:
Ok. #prabhakar-maity, my recommendation based in your case is to use a #ExceptionHandler or #ControllerAdvice instead of try...catch. For example, you have 6 methods in your controller or several controllers and want to handle the same exception (Internal Server Error) and return the same info, so you’ll have to implement a try..catch in each method, while using #ControllerAdive (multiple controllers) or #ExceptionHandler (one controller) you implement your logic in one place
Check this question for more info LINK
You can reference Spring MVC Test Framework - Unsupported Media Type
You may be missing #EnableWebMvc annotation in your controller.
EDIT - for Comment:
Instead of
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}");
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(),
response.getStatus());
Try:
mockMvc.perform(requestBuilder)
.andExpect(status().isInternalServerError());

Micronaut: Exception Handler is not invoked while executing Mockito tests

When I create a case where exception is thrown by the code by calling APIs, at that time, ExceptionHandler is invoked as expected. But when I try creating the same case through unit tests, at that time, ExceptionHandler is not invoked. My classes are as below:
Controller.java
#Post("/XXX")
public ResponseEntity<List<CategoryTopicBean>> listCategoryTopics(#Body CategoryIdsRequestBean categoryIdsRequestBean) {
if (categoryIdsRequestBean.getCategoryIds().size() > MAX_ALLOWED_CATEGORY_SELECTION) {
throw new CustomException(SystemConstants.ResponseCode.ERROR, SystemConstants.ResponseMessage.OVERFLOW_MAX_CATEGORIES);
}
...
CustomExceptionHandler.java:
#Produces
#Singleton
#Requires(classes = {CustomException.class, ExceptionHandler.class})
public class CustomExceptionHandler implements ExceptionHandler<CustomException, HttpResponse> {
#Override
public HttpResponse handle(HttpRequest request, CustomException exception) {
return HttpResponse.ok(new ResponseEntity<>(exception.responseCode, exception.getMessage()));
}
}
XXXShould.java
#Test
public void should_list_category_topics() {
CategoryIdsRequestBean categoryIdsBean = new CategoryIdsRequestBean();
IdBean idBean = new IdBean();
idBean.setId(ID_1);
categoryIdsBean.setCategoryIds(Arrays.asList(idBean));
ResponseEntity<List<CategoryTopicBean>> responseEntity = topicController.listCategoryTopics(categoryIdsBean);
assertThat(SystemConstants.ResponseCode.SUCCESS).isEqualTo(responseEntity.getResponseCode());
assertThat(1).isEqualTo(responseEntity.getData().size());
categoryIdsBean = new CategoryIdsRequestBean();
categoryIdsBean.setCategoryIds(Arrays.asList(idBean, idBean, idBean, idBean, idBean, idBean));
responseEntity = topicController.listCategoryTopics(categoryIdsBean);
}
Can anyone please look into this, and help me out?
The problem here is, you are testing the controller by directly invoking the controller method
topicController.listCategoryTopics(categoryIdsBean).
This is not a good approach to test controller functionality. What you should do is use MicronautTest. MicronautTest will start an embedded server. Now you can use an HTTP client to hit the endpoint and retrieve the result.
Your code needs to be changed to something around the lines as below.
#MicronautTest
class HelloWorldTest {
#Inject
#Client("/")
RxHttpClient client;
#Test
public void should_list_category_topics() {
// Test Data
CategoryIdsRequestBean categoryIdsBean = new CategoryIdsRequestBean();
IdBean idBean = new IdBean();
idBean.setId(ID_1);
categoryIdsBean.setCategoryIds(Arrays.asList(idBean));
HttpRequest<String> request = HttpRequest.POST("/XXX", categoryIdsBean);
ResponseEntity<List<CategoryTopicBean>> retrieve = client.toBlocking().retrieve(request, ResponseEntity.class);
categoryIdsBean = new CategoryIdsRequestBean();
categoryIdsBean.setCategoryIds(Arrays.asList(idBean, idBean, idBean, idBean, idBean, idBean));
responseEntity = topicController.listCategoryTopics(categoryIdsBean);
}
}
For the exception case scenario, as the exception handler returns ResponseEntity<String>, you would need to make a minor change in the above code.
ResponseEntity<String> retrieve = client.toBlocking()
.retrieve(request, ResponseEntity.class);
If your controller calls any other service, do not forget to mock the behavior.

Spring Boot custom receive responses with HTTP OPTIONS by throw exceptions

I am new to Spring Boot, and I am trying to test a connection using HTTP OPTIONS.
My design is that I have a Service class that contains the logics for the testing. I also have an API Controller class that implements the method from Service.
My currently understanding is that the controller can be used to respond back different HTTP statuses using exceptions.
This is the method I wrote inside the controller for this purpose:
#PostMapping(path = "/test")
public ResponseEntity<Void> testConnection(#RequestBody URL url) {
try {
ControllerService.testConnection(url);
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
} catch (CredentialsException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(null);
} catch (URLException | URISyntaxException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
} catch (UnknownException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
The way exceptions are triggered and the method testConnection() are inside the service class:
public static void testConnection(URL url)
throws URISyntaxException, CredentialsException, URLException, UnknownException {
String authHeaderValue = "Basic " + Base64.getEncoder().encodeToString(("user" + ':' + "password").getBytes());
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Authorization", authHeaderValue);
RestTemplate rest = new RestTemplate();
final ResponseEntity<Object> optionsResponse = rest.exchange(url.toURI(), HttpMethod.OPTIONS, new HttpEntity<>(requestHeaders), Object.class);
int code = optionsResponse.getStatusCodeValue();
if (code == 403) {
throw new InvalidCredentialsException();
} else if (code == 404) {
throw new InvalidURLException();
} else if (code == 500) {
throw new UnknownErrorException();
} else if (code == 200){
String message = "Test connection successful";
LOGGER.info(message);
}
}
I have created those custom exception classes.
Is this the proper way to trigger the right HTTP response inside the controller method or does Spring Boot has some other design? If so, is my list of exceptions comprehensive enough or do I need to add more to the testConnection() method in the service class?
You can write ExceptionHandler for each of the Exception type, so you don't have to repeat the code or use try/ catch block at all. Just let your testConnection and other methods to throw the exception.
import org.springframework.web.bind.annotation.ExceptionHandler;
#ExceptionHandler(CredentialsException.class)
public void credentialsExceptionHandler(CredentialsException e, HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.FORBIDDEN.value(), e.getMessage());
}
There are different ways to define and use the ExceptionHandler method. But conceptually same.

How to mock Jersey REST client to throw HTTP 500 responses?

I am writing a Java class that uses Jersey under the hood to send an HTTP request to a RESTful API (3rd party).
I would also like to write a JUnit test that mocks the API sending back HTTP 500 responses. Being new to Jersey, it is tough for me to see what I have to do to mock these HTTP 500 responses.
So far here is my best attempt:
// The main class-under-test
public class MyJerseyAdaptor {
public void send() {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
String uri = UriBuilder.fromUri("http://example.com/whatever").build();
WebResource service = client.resource(uri);
// I *believe* this is where Jersey actually makes the API call...
service.path("rest").path("somePath")
.accept(MediaType.TEXT_HTML).get(String.class);
}
}
#Test
public void sendThrowsOnHttp500() {
// GIVEN
MyJerseyAdaptor adaptor = new MyJerseyAdaptor();
// WHEN
try {
adaptor.send();
// THEN - we should never get here since we have mocked the server to
// return an HTTP 500
org.junit.Assert.fail();
}
catch(RuntimeException rte) {
;
}
}
I am familiar with Mockito but have no preference in mocking library. Basically if someone could just tell me which classes/methods need to be mocked to throw a HTTP 500 response I can figure out how to actually implement the mocks.
Try this:
WebResource service = client.resource(uri);
WebResource serviceSpy = Mockito.spy(service);
Mockito.doThrow(new RuntimeException("500!")).when(serviceSpy).get(Mockito.any(String.class));
serviceSpy.path("rest").path("somePath")
.accept(MediaType.TEXT_HTML).get(String.class);
I don't know jersey, but from my understanding, I think the actual call is done when get() method is invoked.
So you can just use a real WebResource object and replace the behavior of the get(String) method to throw the exception instead of actually execute the http call.
I'm writing a Jersey web application... and we throw WebApplicationException for HTTP error responses. You can simply pass the response code as the constructor-parameter. For example,
throw new WebApplicationException(500);
When this exception is thrown server-side, it shows up in my browser as a 500 HTTP response.
Not sure if this is what you want... but I thought the input might help! Best of luck.
I was able to simulate a 500 response with the following code:
#RunWith(MockitoJUnitRunner.class)
public class JerseyTest {
#Mock
private Client client;
#Mock
private WebResource resource;
#Mock
private WebResource.Builder resourceBuilder;
#InjectMocks
private Service service;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void jerseyWith500() throws Exception {
// Mock the client to return expected resource
when(client.resource(anyString())).thenReturn(resource);
// Mock the builder
when(resource.accept(MediaType.APPLICATION_JSON)).thenReturn(resourceBuilder);
// Mock the response object to throw an error that simulates a 500 response
ClientResponse c = new ClientResponse(500, null, null, null);
// The buffered response needs to be false or else we get an NPE
// when it tries to read the null entity above.
UniformInterfaceException uie = new UniformInterfaceException(c, false);
when(resourceBuilder.get(String.class)).thenThrow(uie);
try {
service.get("/my/test/path");
} catch (Exception e) {
// Your assert logic for what should happen here.
}
}
}

Categories