Skip to content

Instantly share code, notes, and snippets.

@toyg
Last active January 28, 2019 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save toyg/dc5fd6d98cb9b6c65785985d4f3fe748 to your computer and use it in GitHub Desktop.
Save toyg/dc5fd6d98cb9b6c65785985d4f3fe748 to your computer and use it in GitHub Desktop.
Utility methods to mock HTTP calls made with the Apache Commons HttpClient fluent interfaces. It allows simple specification of url, expected response, simple headers, and body. This is enough for my current needs, feel free to suggest other commonly-used sub-interfaces that should be mocked in this scenario.
package com.autoepm.versioner.common;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Response;
import org.apache.http.message.BasicHeader;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
public class MockUtils {
/**
* generic utility to mock <i>simple</i> http calls made with Apache Commons interfaces.
*
* @param client mocked Executor instance that will be called by tested code to make requests
* @param regexCall regular expression to match call, e.g. "GET (.*)/workspace/index.jsp(.*)"
* @param responseCode http status code to return
* @throws IOException
*/
public static void mockHttpCall(Executor client, String regexCall, int responseCode) throws IOException {
mockHttpCall(client, regexCall, responseCode, new HashMap<String, String>(), "");
}
/**
* generic utility to mock <i>detailed</i> http calls made with Apache Commons interfaces.
*
* @param client mocked Executor instance that will be called by tested code to make requests
* @param regexCall regular expression to match call, e.g. "POST (.*)/workspace/index.jsp(.*)"
* @param responseCode http status code to return
* @param responseHeaders Map of headers the response will return
* @param body response body
* @throws IOException
*/
public static void mockHttpCall(Executor client,
String regexCall,
int responseCode,
Map<String, String> responseHeaders,
String body) throws IOException {
HttpEntity entity = mockHttpEntity(client, regexCall, responseCode, responseHeaders);
Mockito.when(entity.getContent()).thenReturn(
new ByteArrayInputStream(body.getBytes()));
}
/**
* generic utility to mock <i>binary</i> http calls made with Apache Commons interfaces.
*
* @param client mocked Executor instance that will be called by tested code to make requests
* @param regexCall regular expression to match call, e.g. "POST (.*)/workspace/index.jsp(.*)"
* @param responseCode http status code to return
* @param responseHeaders Map of headers the response will return
* @param filePath Path of file to "serve"
* @throws IOException
*/
public static void mockHttpCallBinary(Executor client,
String regexCall,
int responseCode,
Map<String, String> responseHeaders,
Path filePath) throws IOException {
HttpEntity entity = mockHttpEntity(client, regexCall, responseCode, responseHeaders);
ArgumentCaptor<HttpEntity> entityArgument = ArgumentCaptor.forClass(HttpEntity.class);
InputStream fis = Files.newInputStream(filePath, StandardOpenOption.READ);
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) throws IOException {
BufferedOutputStream bos = invocation.getArgument(0);
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) > 0) {
bos.write(buffer, 0, length);
}
return null;
}
}).when(entity).writeTo(any());
Mockito.when(entity.getContent()).thenReturn(fis);
}
/**
* common stubbing
*
* @param client mocked Executor instance that will be called by tested code to make requests
* @param regexCall regular expression to match call, e.g. "POST (.*)/workspace/index.jsp(.*)"
* @param responseHeaders Map of headers the response will return
* @param responseCode http status code to return
* @return HttpEntity that can be further customized
* @throws IOException
*/
private static HttpEntity mockHttpEntity(Executor client, String regexCall, int responseCode, Map<String, String> responseHeaders) throws IOException {
Response response = mock(Response.class);
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
HttpEntity entity = mock(HttpEntity.class);
Header[] headers = responseHeaders.entrySet().stream().map(
header -> new BasicHeader(header.getKey(), header.getValue())
).collect(Collectors.toList()).toArray(new Header[responseHeaders.size()]);
Mockito.when(client.execute(argThat(
req -> (req != null) && (req.toString().matches(regexCall))
))).thenReturn(response);
Mockito.when(response.returnResponse()).thenReturn(httpResponse);
Mockito.when(httpResponse.getStatusLine()).thenReturn(statusLine);
Mockito.when(statusLine.getStatusCode()).thenReturn(responseCode);
Mockito.when(httpResponse.getAllHeaders()).thenReturn(headers);
ArgumentCaptor<HttpResponse> argument = ArgumentCaptor.forClass(HttpResponse.class);
doAnswer(
invocation -> new BasicHeader(
invocation.getArgument(0).toString(),
responseHeaders.get(invocation.getArgument(0).toString()))
).when(httpResponse).getFirstHeader(anyString());
Mockito.when(httpResponse.getEntity()).thenReturn(entity);
return entity;
}
}
/**
* Copyright (c) 2019 Giacomo Lacava
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
**/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment