MockWebserver and Retrofit 2.0 - java

Trying to unit test a Retrofit 2.0 response using MockWebServer. I have the webserver setup, but the problem occurs when i am trying to pass a mock json file as a response. To illustrate, my folder structure is:
src->test->java->package_name->class_name.java
src->test->resources->list_success.json
My unit test:
#Before
public void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
server = new MockWebServer();
serviceHelper = new ServiceHelper();
}
#Test
public void testEventBusIdPostedOnSuccessfulServiceCall() throws Exception {
server.start();
server.enqueue(new MockResponse().setResponseCode(200).setBody(getStringFromFile(RuntimeEnvironment.application, "list_success.json")));
MainActivity.URL = server.url("/").toString();
serviceHelper.getIndividualData("Store","7");
verify(eventBus).post(any());
}
#After
public void tearDown() throws Exception{
server.shutdown();
}
Everything works fine. The retrofit call which is async is triggered with the mock JSON. However, the callback never gets triggered. I tried putting in a countdown latch within the actual service implementation, but nothing happens either.
public void getIndividualData(String item, String number) {
Call<DataList> dataList = RestClient.get().getList(item, number);
dataList.enqueue(new Callback<DataList>() {
#Override
public void onResponse(Response<DataList> response, Retrofit retrofit) {
/*data persistence should take place before sending out the eventbus message.
Passing the response object directly for sample purpose. */
EventBus.getDefault().post(new IndividualItemEvent(response.body().Values));
// latch.countDown(); ---->might be needed for retrofit async unit testing
}
#Override
public void onFailure(Throwable t) {
Log.d("%%%%%", "retrofit failure");
// latch.countDown(); ---->might be needed for retrofit async unit testing
}
});
// try {
// latch.await();
// } catch (InterruptedException e) { ---->might be needed for retrofit async unit testing
// e.printStackTrace();
// }
Am i missing something here?
Thanks.

Related

Exception Handling in JAX-RS

java 8, spring, rest
I am trying to capture the Response that comes from exception mapper, and do something with it in the caller which throws the exception. Thanks.
#Provider
public class CustomerExceptionHandler implements ExceptionMapper<CustomerException>
{
#Override
public Response toResponse(CustomerException exception)
{
return Response.status(Status.BAD_REQUEST).entity(CustomerException.getMessage()).build();
}
}
public class CustomerException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public CustomerException() {
super();
}
public CustomerException(String msg) {
super(msg);
}
public CustomerException(String msg, Exception e) {
super(msg, e);
}
}
public class ExceptionDemo{
public void getExceptionResponse(){
//do something
throw new CustomerException("Something is wrong");// CustomerExceptionHandler is going to return me a Response, how can I capture the response here?
//capture response and do something with it
}
}
I'm not sure ExceptionMappers work in the way you think they do.
When some code in the endpoint throws an exception, and this exception percolates all the way out of the endpoint and back into the container itself (Spring in this case), then the registered ExceptionMappers are consulted to see if they match the thrown exception, and the relevant one's public Response toResponse(T e) {} method is called to transform it into a Response.
The ExceptionMapper doen't get called as part of your endpoint code, and you won't be able to take action based on its resultant Response because it hasn't yet been called. You just need to throw the exception out of the endpoint.

Mocking endpoint response in camel

I have a camel route that sends to loadbalancer and processes the response. Is it possible to mock that response somehow in unit test? I tried to use velocity but it doesn't seem to work in unit tests.
Apache already takes care of such testing requirements. There is adviceWith construct which will solve this problem.
Quoting the example directly with few modifications from the link mentioned above:
#Test
public void testAdvised() throws Exception {
context.addRoutes(new RouteBuilder() {
#Override public void configure() throws Exception {
from("direct:start").id("my-route").to("mock:foo");
}
});
context.getRouteDefinition("my-route").adviceWith(context, new RouteBuilder() {
#Override
public void configure() throws Exception {
// intercept sending to mock:foo and do something else
interceptSendToEndpoint("mock:foo")
.skipSendToOriginalEndpoint()
.to("log:foo")
.to("mock:result");
}
});
getMockEndpoint("mock:foo").expectedMessageCount(0);
getMockEndpoint("mock:result").expectedMessageCount(1);
template.sendBody("direct:start", "Hello World");
assertMockEndpointsSatisfied();
}
Now here a route is defined as:
from("direct:start").id("my-route").to("mock:foo");
And let's say we want to mock the to part here.
This is precisely doing it for me:
context.getRouteDefinition("my-route").adviceWith(context, new RouteBuilder() {
#Override
public void configure() throws Exception {
// intercept sending to mock:foo and do something else
interceptSendToEndpoint("mock:foo")
.skipSendToOriginalEndpoint()
.to("log:foo")
.to("mock:result");
}
});
We get the reference of the route definition we want to modify from CamelContext and using the adviceWith method we can define what all action needs to be done. As here, I advised not to send to actual destination i.e. mock:foo, rather send to two other routes log:foo and mock:result.
Hope it answers your query.

Camel testing - java.lang.IllegalArgumentException: defaultEndpoint must be specified

I am trying to create testcases for my camel route using http://camel.apache.org/mock.html . I need to verify the the processors in the route. But the simple test is not working for me.
public class CamelRouteTest extends CamelTestSupport {
#Override
public String isMockEndpointsAndSkip() {
// override this method and return the pattern for which endpoints to mock,
// and skip sending to the original endpoint.
return "mock:result";
}
#Test
public void verifyMessageCount() throws Exception {
template.sendBody("Test");
getMockEndpoint("mock:result").expectedMessageCount(1);
assertMockEndpointsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").to("mock:result");
}
};
}
}
Stacktrace:
java.lang.IllegalArgumentException: defaultEndpoint must be specified
at org.apache.camel.util.ObjectHelper.notNull(ObjectHelper.java:308)
at org.apache.camel.impl.DefaultProducerTemplate.getMandatoryDefaultEndpoint(DefaultProducerTemplate.java:506)
at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:370)
The template.sendBody("Test") try to send Test to the default endpoint. As in your code this is not configured it fails.
You could:
specify which endpoint to use
template.sendBody("direct:start", "Test");
get an endpoint from the context and set it as the default endpoint
Endpoint endpoint = context.getEndpoint("direct:start");
template.setDefaultEndpoint(endpoint);
template.sendBody("Test");

retrofit invoking asynchronous call synchronously

This looks weird but I ended up in this situation. Implemented Restful API call using Retrofit asynchronously. Now there is a sudden requirement change and have to call API one after the other (One at a time), so that in the second API call I have to send session token received from the previous response. One way is to make every API call as synchronous but it takes time to implement this change.
I have tried :
Used setExecutor(Executors.newSingleThreadExecutor(),new
MainThreadExecutor) for RestAdapter.Builder.This didn't work
since API calls are asynchronous and before getting response for the
previous API call second call is made. So the second request has
invalid session token.
In the class where I have implemented all Restful Web services,
used Executors.newSingleThreadExecutor() , this also didn't work
for the same reason.
Could anybody suggest how to resolve this with minimal changes.
Webservice Manager is as below, this is partial and there are many more api's like login:
public class WebServiceManager {
private static final String ROOT_PATH = Urls.REST_ROOT_URL;
RestAdapter restAdapter;
WebServiceInterface webServiceInterface;
private String requestKey;
private String sessionId;
private Context context;
public WebServiceManager(Context context) {
this.context = context;
initializeWebServiceAdapter();
}
private void initializeWebServiceAdapter() {
restAdapter = new RestAdapter.Builder()
.setEndpoint(ROOT_PATH)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
webServiceInterface = restAdapter.create(WebServiceInterface.class);
}
private void setHeaderValues(BaseModel model) {
SessionManager sm= context.getApplicationContext().getSessionManager();
model.getRequestHeader().setRequestKey(sm.getRequestKey());
model.getRequestHeader().setSessionId(sm.getSessionId());
}
public void login(String emailID, String passwd, final WebServiceCallback loginModelWebServiceCallback) {
LoginModel model = RestRequest.getLoginModel(emailID, passwd);
setHeaderValues(model);
webServiceInterface.login(model, new Callback() {
#Override
public void success(LoginModel loginModel, Response response) {
if (loginModelWebServiceCallback != null)
{
SessionManager sm= context.getApplicationContext().getSessionManager();
sm.setSessionDetails(response.getRequestKey(),response.getSessionId());
loginModelWebServiceCallback.success(loginModel);
}
}
#Override
public void failure(RetrofitError error) {
if (loginModelWebServiceCallback != null)
loginModelWebServiceCallback.failure(error);
}
});
}
}
The Executor doesn't matter since you're always invoking the Retrofit service with the Callback argument, which makes it asynchronous. If you want your Retrofit call to be synchronous then the service call method needs a return type, not void. You can read this in the docs.
Once you make your API calls synchronous and ordered how you want, you can wrap them in a Runnable and let an Executor handle the threading for you.
A request can be made in the response from first API itself, when some parameter is expected for the second api call. Have a look at the sample :
public void login(String emailID, String passwd, final WebServiceCallback loginModelWebServiceCallback) {
LoginModel model = RestRequest.getLoginModel(emailID, passwd);
setHeaderValues(model);
webServiceInterface.login(model, new Callback() {
#Override
public void success(LoginModel loginModel, Response response) {
if (loginModelWebServiceCallback != null) {
makeSecondAPIcall();
}
}
#Override
public void failure(RetrofitError error) {
if (loginModelWebServiceCallback != null)
loginModelWebServiceCallback.failure(error);
}
});
}

How to TDD for Restful client code example

I did some TDDs before, but they were just straightforward and simple.
However, I will implement a restful client and invoke a restful API of third parties (Twitter, or Jira).
I used Resteasy client framework to implement that. The code is:
public void invokePUT() {
ClientRequest request =
new ClientRequest("http://example.com/customers");
request.accept("application/xml");
ClientResponse<Customer> response = request.put(Customer.class);
try {
if (response.getStatus() != 201)
throw new RuntimeException("Failed!");
} finally {
response.releaseConnection();
}}
If I want to write a test for this method (should write test before implement this method), what kind of the code should I write.
For GET, I can test the return Entity is equals to my expected entity and for POST, I can test the created entity's id is not null.
But how about for PUT and DELETE. Thanks.
Try to use REST Assured testing framework. It is great tool for testing REST services. On their website you'll find tons of examples how to use it. Just use it together with JUnit or TestNG to check assertions and you are done.
Here's how I'd go about the problem in the short term:
1) Extract the request into a parameter to the method. invokePUT() now becomes:
public void invokePUT(ClientRequest request) {
request.accept("application/xml");
ClientResponse<Customer> response = request.put(Customer.class);
try {
if (response.getStatus() != 201)
throw new RuntimeException("Failed!");
} finally {
response.releaseConnection();
}
}
2) In your test, use a stubbed version of ClientRequest
#Test
public void sendsPayloadAsXml() {
StubbedClientRequest request = new StubbedClientRequest(new StubbedResponse());
restApi.invokePUT(request);
assertEquals("application/xml", request.acceptHeader);
}
#Test
public void makesTheCallUsingPut() {
StubbedClientRequest request = new StubbedClientRequest(new StubbedResponse());
restApi.invokePUT(request);
assertTrue(request.putWasCalled);
}
#Test
public void releasesTheConnectionWhenComplete() {
StubbedResponse success = new StubbedResponse();
StubbedClientRequest request = new StubbedClientRequest(success);
restApi.invokePUT(request);
assertTrue(success.connectionWasClosed);
}
#Test(expected = RuntimeException.class)
public void raisesAnExceptionWhenInvalidResponseReceived() {
StubbedClientRequest request = new StubbedClientRequest(new StubbedResponse(400));
restApi.invokePUT(request);
}
private static class StubbedClientRequest extends ClientRequest {
public String acceptHeader = "";
public boolean putWasCalled;
public ClientResponse response
public StubbedRequest(ClientResponse response) {
this.response = response;
}
#Override
public ClientResponse put(Class klass) {
putWasCalled = true;
return response;
}
#Override
public void accept(String header) {
acceptHeader += header;
}
}
private static class StubbedResponse extends ClientResponse {
public boolean connectionWasReleased;
public int status = 201;
public StubbedResponse(int status) {
this.status = status;
}
public StubbedResponse() { }
}
This may not be a perfect design (Handing the ClientRequest to the class and having the RestEasy stuff exposed to the outside world) but it's a start.
Hope that helps!
Brandon
i would inject mocked classes that test, if put and delete was called as intended (with expected parameters and so on). easymock or similar is good for that
(same with post and get)
EDIT:
in case you want to test the rest client, use dependency injection to inject the request, then use easymock to mock it like this (for example to test, if delete is called properly):
#Test void myTest(){
ClientRequest mock = EasyMock.createMock(ClientRequest.class);
mock.delete(2); //test if resource with id=2 is deleted or something similar
EasyMock.replay(mock);
invokeDelete(mock);
EasyMock.verify(mock);
}

Categories