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.
}
}
}
Related
I have created a custom extension (Connector), which sends an HttpRequest (using org.mule.runtime.http.api.client.HttpClient and the related classes).
The extension's unit tests file contains the following test, to which I've added a simple Mockito mock to throw a TimeoutException when the HTTP request is being sent:
public class DemoOperationsTestCase extends MuleArtifactFunctionalTestCase {
/**
* Specifies the mule config xml with the flows that are going to be executed in the tests, this file lives in the test resources.
*/
#Override
protected String getConfigFile() {
return "test-mule-config.xml";
}
#Test
public void executeSayHiOperation() throws Exception {
HttpClient httpClient = mock(HttpClient.class);
HttpRequest httpRequest = mock(HttpRequest.class);
when(httpClient.send(any(HttpRequest.class), anyInt(), anyBoolean(), any(HttpAuthentication.class))).thenThrow(new TimeoutException());
String payloadValue = ((String) flowRunner("sayHiFlow").run()
.getMessage()
.getPayload()
.getValue());
assertThat(payloadValue, is("Hello Mariano Gonzalez!!!"));
}
}
The test should fail because the function should throw a TimeoutException, it is what I want for now.
The code that is being tested is as follows (redacted for convenience):
HttpClient client = connection.getHttpClient();
HttpResponse httpResponse = null;
String response = "N/A";
HttpRequestBuilder builder = HttpRequest.builder();
try {
httpResponse = client
.send(builder
.addHeader("Authorization", authorization)
.method("POST")
.entity(new ByteArrayHttpEntity("Hello from Mule Connector!".getBytes()))
.uri(destinationUrl)
.build(),
0, false, null);
response = IOUtils.toString(httpResponse.getEntity().getContent());
} catch (IOException e) {
e.printStackTrace();
throw new ModuleException(DemoError.NO_RESPONSE, new Exception("Failed to get response"));
} catch (TimeoutException e) {
e.printStackTrace();
throw new ModuleException(DemoError.NO_RESPONSE, new Exception("Connection timed out"));
}
But I always get the "Failed to get response" error message, which is what I get when I run the Connector with a nonexistent server, therefore the mock isn't working (it actually tries to send an HTTP request).
I am new to Java unit testing, so it might be a general mocking issue and not specific to MuleSoft - though I came across other questions (such as this one and this one), I tried the suggestions in the answers and the comments, but I get the same error. I even tried to use thenReturn instead of thenThrow, and I get the same error - so the mock isn't working.
Any idea why this is happening?
I have an endpoint that makes a request to a server to check the status. I use WebClient with vertex. I create a client like this.
WebClient client = WebClient.create(vertx);
and the request looks like the following
client.get(check.url).send(ar -> {
HttpResponse<Buffer> resp = ar.result();
if (resp != null) {
check.response = resp.bodyAsString();
} else {
check.response = "";
}
logger.debug("Received response from app: {} with result:{} on url:{} : {}",
check.appName, check.result, check.url, check.response);
check.result.complete(ar.succeeded());
});
The problem i have is when the client tries to send the request.
EDIT: I want to mock that client.send() and return my WebClient with the status I want like 200 or 400. Please be advised that I can't access this WebClient as it's declared under the method I want to test.
I have this test right now that I'm trying to fix.
TEST
#Test
public void testWongUrlsAndNormalMode() throws Exception{
when(healthCheckEndpoint.configuration.getMode()).thenReturn(HealthCheckerConfiguration.HealthCheckMode.NORMAL);
when(healthCheckEndpoint.configuration.getHealthCheckUrls()).thenReturn(urls);
Response a = healthCheckEndpoint.checkApplications(); //here is when the client.get(url).send() returns nullPointerException
assertEquals(a.getStatus(), 500);
assertEquals(a.getEntity(), "normal mode no app urls");
}
but as mentioned i get a null pointer exception when the WebClient tries to send the request.
I have also tried to mock the WebClient but I can't mock the send() method as it is a void method
When calling a JAX-WS endpoint, how can I get the HTTP response code?
In the sample code bellow, when calling the web service at port.getCustomer(customerID); an Exception may be thrown, such as 401 or 500.
In such cases, how can I get the HTTP status code from the HTTP response?
#Stateless
public class CustomerWSClient {
#WebServiceRef(wsdlLocation = "/customer.wsdl")
private CustomerService service;
public void getCustomer(Integer customerID) throws Exception {
Customer port = service.getCustomerPort();
port.getCustomer(customerID); // how to get HTTP status
}
}
Completing #Praveen answer, you have to turn the port into a raw BindingProvider and then get the values from the context.
Don't forget that transaction will be marked for rollback if an exception occours in your managed web service client.
#Stateless
public class CustomerWSClient {
#WebServiceRef(wsdlLocation = "/customer.wsdl")
private CustomerService service;
public void getCustomer(Integer customerID) throws Exception {
Customer port = service.getCustomerPort();
try {
port.getCustomer(customerID);
} catch(Exception e) {
throw e;
} finally {
// Get the HTTP code here!
int responseCode = (Integer)((BindingProvider) port).getResponseContext().get(MessageContext.HTTP_RESPONSE_CODE);
}
}
}
The below post is similar to your question. Hopefully it should work for you
https://stackoverflow.com/a/35914837/4896191
I've written a Jersey Server application and a Client application which is consuming the provided REST Services.
But I've problems to pass exception messages from server to client.
Currently I've implemented it like:
Server WS Method:
#GET
#Path("/test")
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public TestModel doTest(){
throw new NotImplementedException("[doTest] is not implemented yet");
}
NotImplementedException:
public class NotImplementedException extends WebApplicationException{
public NotImplementedException(){
super(Response.status(Response.Status.NOT_IMPLEMENTED)
.entity("The operation you've called is not implemented yet").type(MediaType.APPLICATION_JSON).build());
}
public NotImplementedException(String message){
super(Response.status(Response.Status.BAD_GATEWAY).entity(message).type(MediaType.APPLICATION_JSON).build());
}
}
Client:
public static TestModel doTest() throws Exception{
try {
Client client = getClient();
WebTarget webTarget = client.target("server..../");
WebTarget getGuTarget = webTarget.path("test");
Invocation.Builder ib = getGuTarget.request(MediaType.APPLICATION_JSON);
TestModel response = ib.get(TestModel.class); //here exception is thorwn
return response;
} catch (Exception e) {
throw e;
}
}
The exception caught on the Client looks like:
javax.ws.rs.ServerErrorException: HTTP 502 Bad Gateway
at org.glassfish.jersey.client.JerseyInvocation.createExceptionForFamily(JerseyInvocation.java:1029)
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1009)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:799)
at org.glassfish.jersey.client.JerseyInvocation.access$500(JerseyInvocation.java:91)
Unfortunately I'm not able to receive the "[doTest] is not implemented yet" Message on the client. How I can get this message?
When I test the webservice I receive the correct message in the body. Unfortunately I don't know how I can access it via jersey?
Thanks.
You can use an ExceptionMapper for custom exceptions: https://jersey.java.net/apidocs/2.11/jersey/javax/ws/rs/ext/ExceptionMapper.html
Otherwise, Jersey tries to map exceptions as good as it can on its own.
I have a number of classes exposed as JAX-RS request "handlers", using javax.ws.rs.Path annotations. I want to add certain actions before every request and after each request. Also, I need to create a global application-wide exception handler, which will catch everything thrown by these handlers and protocol.
Is it possible to achieve this with standard JAX-RS without creating of a custom class inherited from com.sun.jersey.spi.container.servlet.ServletContainer (I'm using Jersey).
You can also use ExceptionMappers. This mechanism which catch the exception thrown by your service and convert it to the appropriate Response:
#Provider
public class PersistenceMapper implements ExceptionMapper<PersistenceException> {
#Override
public Response toResponse(PersistenceException arg0) {
if(arg0.getCause() instanceof InvalidDataException) {
return Response.status(Response.Status.BAD_REQUEST).build();
} else {
...
}
}
}
For more information see:
JAX-RS using exception mappers
You could create a proxy RESTful service and use this as the entry point to all your other RESTful services. This proxy can receive requests, do any pre-processing, call the RESTful service required, process the response and then return something to the caller.
I have a set up like this in a project I've been working on. The proxy performs functions like authentication, authorisation and audit logging. I can go into further details if you like.
Edit:
Here is an idea of how you might want to implement a proxy that supports GET requests;
#Path("/proxy")
public class Proxy
{
private Logger log = Logger.getLogger(Proxy.class);
#Context private UriInfo uriInfo;
#GET
#Path("/{webService}/{method}")
public Response doProxy(#Context HttpServletRequest req,
#PathParam("webService") String webService,
#PathParam("method") String method)
{
log.debug("log request details");
//implement this method to work out the URL of your end service
String url = constructURL(req, uriInfo, webService, method);
//Do any actions here before calling the end service
Client client = Client.create();
WebResource resource = client.resource(url);
try
{
ClientResponse response = resource.get(ClientResponse.class);
int status = response.getStatus();
String responseData = response.getEntity(String.class);
log.debug("log response details");
//Do any actions here after getting the response from the end service,
//but before you send the response back to the caller.
return Response.status(status).entity(responseData).build();
}
catch (Throwable t)
{
//Global exception handler here
//remember to return a Response of some kind.
}
}
You can use filters to read and modify all requests and responses.