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);
}
Related
Context
I am currently working on a JavaEE project with a lot of existing resource based JAX-RS services. For this project we would like to have batch processing to prevent a lot of separate calls and, most importantly, to execute these different methods in a transactional context for rollback purposes with the native MongoDB driver. We want to avoid manually creating new methods for all possible combinations. I could not find any solution to this issue on Stack Overflow so I started analyzing the implementation of RESTEasy and I came up with the following solution.
Below a simplified/pseudo version of my code:
JAX-RS method
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("execute")
public Response executeBatch(BatchRequestWrapper batchRequestWrapper) throws UnsupportedEncodingException
{
// Retrieve information from context
HttpServletRequest httpServletRequest = ResteasyProviderFactory.getContextData(HttpServletRequest.class);
HttpServletResponse httpServletResponse = ResteasyProviderFactory.getContextData(HttpServletResponse.class);
ServletContext servletContext = ResteasyProviderFactory.getContextData(ServletContext.class);
HttpResponse httpResponse = ResteasyProviderFactory.getContextData(HttpResponse.class);
SynchronousDispatcher dispatcher = (SynchronousDispatcher) ResteasyProviderFactory.getContextData(Dispatcher.class);
ResteasyHttpHeaders httpHeaders = (ResteasyHttpHeaders) ResteasyProviderFactory.getContextData(HttpHeaders.class);
ResteasyUriInfo uriInfo = (ResteasyUriInfo) ResteasyProviderFactory.getContextData(UriInfo.class);
// Create Mongo Client Session object and save it in a Singleton which contains a ThreadLocal object so that DAO layer can reuse the client session object for all methods.
// Iterate over all the methods and invoke dispatcher
for (BatchRequest batchRequest : batchRequestWrapper.getBatchRequests())
{
// Update URI based on specific endpoint
uriInfo.setRequestUri(URI.create(batchRequest.getUri()));
// Temporary use mock response for the response
MockHttpResponse response = new MockHttpResponse();
// Create httpservletinput message from RESTEasy lib to pass to the dispatcher. It will automatically resolve all parameters/methods etc.
HttpServletInputMessage request = new HttpServletInputMessage(httpServletRequest, httpServletResponse, servletContext, httpResponse, httpHeaders, uriInfo, batchRequest.getHttpMethod(), dispatcher);
// Set body in input stream if body is specified. This will inject the correct 'body' parameters in the methods. Query and Path parameters are already resolved in the method above.
if(!Strings.isNullOrEmpty(batchRequest.getBody()))
{
InputStream targetStream = new ByteArrayInputStream(batchRequest.getBody().getBytes(StandardCharsets.UTF_8));
request.setInputStream(targetStream);
}
// Actual invoke
dispatcher.invoke(request, response);
// Do something with response object
}
// Clean or abort session based on invoke result
return Response.ok().entity(null).build();
}
Request Object
public class BatchRequestWrapper
{
private List<BatchRequest> batchRequests;
public List<BatchRequest> getBatchRequests()
{
return batchRequests;
}
public void setBatchRequests(List<BatchRequest> batchRequests)
{
this.batchRequests = batchRequests;
}
}
public class BatchRequest
{
private String uri;
private String httpMethod;
private String body;
public String getUri()
{
return uri;
}
public void setUri(String uri)
{
this.uri = uri;
}
public String getHttpMethod()
{
return httpMethod;
}
public void setHttpMethod(String httpMethod)
{
this.httpMethod = httpMethod;
}
public String getBody()
{
return body;
}
public void setBody(String body)
{
this.body = body;
}
}
My solution works with one new REST method and let's me reuse all the existing JAX-RS annotated methods in the project. Before I actually fully implement this and bring it to production, I would like to know if this is the way to actually do this or are there better alternatives? I am not a big fan of the hard dependency on RESTEasy though.
I have a service class that calls a REST API to get, create, update and delete subscribers. The Uri remains the same, but the HTTP method changes as you'd expect. I want to test the correct method is given. Below is an example of the updateSubscriber and its test.
public class MyService {
HttpClient httpClient;
public MyService(HttpClient httpClient) {
this.httpClient = httpClient;
}
//...
public int updateSubscriber(Subscriber subscriber) throws ... {
// PUT is the correct method for this request
HttpResponse response = httpClient.execute( new HttpPut( "https://example.org/api/subscribers" ) );
//...
}
//...
Here is my test with JUnit and Mockito:
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest
{
#Mock
private HttpClient mockHttpClient;
#Mock
private HttpResponse mockResponse;
#Mock
private StatusLine mockStatusline;
#Mock
private HttpEntity mockEntity;
// test subject
private MyService myService;
#Before
public void setup() {
// // this will just ensure http* objects are returning our mocked instances so we can manipulate them..
// when(mockHttpClient.execute(any(HttpGet.class))).thenReturn(mockResponse);
// when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse);
// when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);
// when(mockHttpClient.execute(any(HttpDelete.class))).thenReturn(mockResponse);
// when(mockResponse.getStatusLine()).thenReturn(mockStatusline);
// when(mockStatusline.getStatusCode()).thenReturn(HttpStatus.SC_OK);
myService = new MyService(mockHttpClient);
}
#Test
public void testUpdateSubscriber() throws ...
{
when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);
when(mockResponse.getStatusLine()).thenReturn(mockStatusline);
when(mockStatusline.getStatusCode()).thenReturn(HttpStatus.SC_OK);
String responseString = "...";
// this is consumed by a static method which we cannot mock, so we must deal with an actual entity instance
BasicHttpEntity entity = new BasicHttpEntity();
entity.setContent(new ByteArrayInputStream(responseString.getBytes()));
when(mockResponse.getEntity()).thenReturn(entity);
// create a test case Subscriber instance
Subscriber subscriber = new Subscriber();
int statusCode = myService.updateSubscriber(subscriber);
assertEquals(HttpStatus.SC_OK, statusCode);
// just confirm that an HTTP request was made
// TODO this isn't working, still passes when wrong Http* method used
verify(mockHttpClient, times(1)).execute(any(HttpPut.class));
}
//...
However, when I (wrongfully) have the another Http* method instance, it still passes:
// this is wrong, and should fail, but passed :(
HttpResponse response = httpClient.execute( new HttpGet( "https://example.org/api/subscribers" ) );
I'd really like to be able to test this as the action performed could be wrong if the method is mistaken. This test is to ensure that the PUT method was correctly used with the HTTP request for updateSubscriber. Any ideas?
Test passes because HtppPut and HttpGet both are implementation classes of HttpRequestBase, Change the mocking from HttpRequestBase class to HttpPut class
when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);
So now if you try with GET call Test will fail with NullPointerException since GET call has no stub
Not sure if this is the proper answer to my question but I got managed to get the tests to work as intended using a custom argument matcher:
package uk.ac.strath.matchers;
import org.apache.http.client.methods.HttpUriRequest;
import org.mockito.ArgumentMatcher;
public class HttpMethodMatcher implements ArgumentMatcher<HttpUriRequest> {
private String expectedClassName;
// constructors
public HttpMethodMatcher(String expectedClassName) {
this.expectedClassName = expectedClassName;
}
#Override
public boolean matches(HttpUriRequest httpMessage) {
if (httpMessage.getClass().getName().equals(expectedClassName)) {
return true;
}
return false;
}
}
Now in my test, I can do:
verify(mockHttpClient, times(1)).execute( argThat(new HttpMethodMatcher( HttpGet.class.getName() )) );
This tutorial was helpful: https://www.baeldung.com/mockito-argument-matchers
In my jersey-2 application I'm using a very simple ContainerRequestFilter that will check for basic authentication (probably reinventing the wheel, but bear with me). Filter goes somewhat like this
#Override
public void filter(ContainerRequestContext context) throws IOException {
String authHeader = context.getHeaderString(HttpHeaders.AUTHORIZATION);
if (StringUtils.isBlank(authHeader)) {
log.info("Auth header is missing.");
context.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.type(MediaType.APPLICATION_JSON)
.entity(ErrorResponse.authenticationRequired())
.build());
}
}
Now I'd like to write a test for it, mocking the ContainerRequestContext object.
#Test
public void emptyHeader() throws Exception {
when(context.getHeaderString(HttpHeaders.AUTHORIZATION)).thenReturn(null);
filter.filter(context);
Response r = Response.status(Response.Status.UNAUTHORIZED)
.type(MediaType.APPLICATION_JSON)
.entity(ErrorResponse.authenticationRequired())
.build();
verify(context).abortWith(eq(r));
}
This test fails on the eq(r) call, even if looking at the string representation of the Response objects they are the same. Any idea what's wrong?
Since I had the same question, I did it like this:
#Test
public void abort() {
new MyFilter().filter(requestContext);
ArgumentCaptor<Response> responseCaptor = ArgumentCaptor.forClass(Response.class);
verify(requestContext).abortWith(responseCaptor.capture());
Response response = responseCaptor.getValue();
assertNotNull(response);
JerseyResponseAssert.assertThat(response)
.hasStatusCode(Response.Status.FORBIDDEN);
}
I don't believe you need the eq() method. You should verify that context. abortWith(r) was called. I might be missing something though because you've not included what eq(r) is.
I was wondering how should I go about injecting a mocks - we have bunch of classes that do server calls, however our CI system can not access external resources and thus will not make a call to a server. Thus, the call has to be simulated and hardcoded values (such as response codes) needed to be return.
So, here is a snippet of a code:
HttpPost httpRequest = new HttPost(uri);
//some code here
try{
httpRequest.setEntity(entity);
HttpResponse response = httpClient.execute(httpRequest);
...
//other, irrelevant, code is here
So, is it possible to inject a mock into httpClient.execute(httpRequest) and return hardcoded response entity from a test unit?
Thank you
Usually mocking some object looks like this:
public class TestClass {
private HttpServer server;
public HttpServer getServer() {
return server;
}
public void setServer(HttpServer server) {
this.server = server;
}
public void method(){
//some action with server
}
}
And test class:
public class TestClassTest {
//class under test
TestClass test = new TestClass();
#org.junit.Test
public void testMethod() throws Exception {
HttpServer mockServer = Mockito.mock(HttpServer.class);
test.setServer(mockServer);
//set up mock, test and verify
}
}
Here you some useful links:
Code example
Official documentation
I have a class which has direct dependency on the RestTemplate. I wish I have a JUnit test of it, offline.
How could I mock a RestTemplate in my unittest?
Sping 3.0 introduced RestTemplate. Since version 3.2, the Spring MVC test framework has provided the class MockRestServiceServer for unit testing client REST code.
I suggest refactoring your client code to remove the direct dependency on RestTemplate, and replace it with references to RestOperations, which is the interface implemented by RestTemplate. and the one you should be coding to.
You can then inject a stub or mock of RestOperations into your code for unit testing, and inject a RestTemplate when using it for real.
You can use the Mock classes in package org.springframework.mock.web.
Usually you will need MockHttpServletRequest and MockHttpServletResponse, but if you need more control you may also need others, e.g. MockRequestDispatcher.
Both of these implement the corresponding Servlet interfaces but add convenience methods for testing (and, most importantly: they work without a real HTTP connection).
You can find the Mock classes in the spring-test jar (accessible through Maven)
Update: it seems that the above classes are no great help for RestTemplate after all. What you will need is to create a mock ClientHttpRequestFactory, and I'm surprised to see that there isn't one in the above package. Here is some code to get you started (haven't tested it):
public class MockClientHttpRequestFactory implements
ClientHttpRequestFactory{
// overwrite this if you want
protected MockClientHttpResponse createResponse(){
return new MockClientHttpResponse();
}
// or this
protected HttpStatus getHttpStatusCode(){
return HttpStatus.OK;
}
// or even this
#Override
public ClientHttpRequest createRequest(final URI uri,
final HttpMethod httpMethod) throws IOException{
return new MockClientHttpRequest(uri, httpMethod);
}
public class MockClientHttpResponse implements ClientHttpResponse{
private final byte[] data = new byte[10000];
private final InputStream body = new ByteArrayInputStream(data);
private final HttpHeaders headers = new HttpHeaders();
private HttpStatus status;
#Override
public InputStream getBody() throws IOException{
return body;
}
#Override
public HttpHeaders getHeaders(){
return headers;
}
#Override
public HttpStatus getStatusCode() throws IOException{
return getHttpStatusCode();
}
#Override
public String getStatusText() throws IOException{
return status.name();
}
#Override
public void close(){
try{
body.close();
} catch(final IOException e){
throw new IllegalStateException(e);
}
}
}
class MockClientHttpRequest implements ClientHttpRequest{
private final HttpHeaders headers = new HttpHeaders();
private final HttpMethod method;
private final URI uri;
private final OutputStream body = new ByteArrayOutputStream();
MockClientHttpRequest(final URI uri, final HttpMethod httpMethod){
this.uri = uri;
method = httpMethod;
}
#Override
public OutputStream getBody() throws IOException{
return body;
}
#Override
public HttpHeaders getHeaders(){
return headers;
}
#Override
public HttpMethod getMethod(){
return method;
}
#Override
public URI getURI(){
return uri;
}
#Override
public ClientHttpResponse execute() throws IOException{
return createResponse();
}
}
}
spring-social-test contains mockup classes that help write tests for RestTemplate. There are also some examples on how to use it within the git repository (e.g. OAuth1TemplateTest).
Please keep in mind that there's currently a Spring feature request (#SPR-7951) to move these classes to spring-web.