Skip to content

Instantly share code, notes, and snippets.

@ahmed-musallam
Last active February 28, 2024 17:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ahmed-musallam/f6238afb3895a23d03b5a09899392108 to your computer and use it in GitHub Desktop.
Save ahmed-musallam/f6238afb3895a23d03b5a09899392108 to your computer and use it in GitHub Desktop.
A simple HTTP server jUnit 5 extension based on https://gist.github.com/rponte/710d65dc3beb28d97655
package com.ahmedmusallam.service;
import java.io.IOException;
import java.net.URI;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class HttpClientService {
/** Perform a get request with the provided credentials provider. */
public String doGet(URI uri, CredentialsProvider credentialsProvider) throws IOException {
if (credentialsProvider == null || uri == null) {
return null;
}
try (CloseableHttpClient httpClient =
HttpClients.custom().setDefaultCredentialsProvider(credentialsProvider).build()) {
HttpGet httpGet = new HttpGet(uri);
try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
return EntityUtils.toString(httpResponse.getEntity());
}
}
}
}
package com.ahmedmusallam.service;
import static org.junit.jupiter.api.Assertions.*;
import com.ahmedmusallam.extension.HttpServerExtension;
import com.ahmedmusallam.extension.JsonSuccessHandler;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class HttpClientServiceTest {
private HttpClientService httpClientService = new HttpClientService();
@RegisterExtension // MUST be static, see: https://junit.org/junit5/docs/current/user-guide/#extensions-registration-programmatic-static-fields
static HttpServerExtension httpServerExtension = new HttpServerExtension();
@Mock
CredentialsProvider credentialsProvider;
@Test
void doGet() throws IOException, URISyntaxException {
assertNull(httpClientService.doGet(null, credentialsProvider));
assertNull(httpClientService.doGet(new URIBuilder().build(), null));
httpServerExtension.registerHandler("/test", new JsonSuccessHandler("{}"));
URI uri = HttpServerExtension.getUriFor("/test");
assertEquals("{}", httpClientService.doGet(uri, new BasicCredentialsProvider()));
}
}
package com.ahmedmusallam.extension;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.http.client.utils.URIBuilder;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
/*
* Note: I chose to implement `BeforeAllCallback` and AfterAllCallback
* but not `AfterEachCallback` and `BeforeEachCallback` for performance reasons.
* I wanted to only run one server per test class and I can register handlers
* on a per-test-method basis. You could implement the `BeforeEachCallback` and `AfterEachCallback`
* interfaces if you really need that behavior.
*/
public class HttpServerExtension implements BeforeAllCallback, AfterAllCallback {
public static final int PORT = 6991;
public static final String HOST = "localhost";
public static final String SCHEME = "http";
private com.sun.net.httpserver.HttpServer server;
@Override
public void afterAll(ExtensionContext extensionContext) throws Exception {
if (server != null) {
server.stop(0); // doesn't wait all current exchange handlers complete
}
}
@Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
server = HttpServer.create(new InetSocketAddress(PORT), 0);
server.setExecutor(null); // creates a default executor
server.start();
}
public static URI getUriFor(String path) throws URISyntaxException{
return new URIBuilder()
.setScheme(SCHEME)
.setHost(HOST)
.setPort(PORT)
.setPath(path)
.build();
}
public void registerHandler(String uriToHandle, HttpHandler httpHandler) {
server.createContext(uriToHandle, httpHandler);
}
}
package com.ahmedmusallam.extension;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import java.net.HttpURLConnection;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
// credit: https://gist.github.com/rponte/710d65dc3beb28d97655#file-httpserverrule-java
public class JsonSuccessHandler implements HttpHandler {
private String responseBody;
private static final String contentType = "application/json";
public JsonSuccessHandler() {}
public JsonSuccessHandler(String responseBody) {
this.responseBody = responseBody;
}
@Override
public void handle(HttpExchange exchange) throws IOException {
exchange.getResponseHeaders().add("Content-Type", contentType);
exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, responseBody.length());
IOUtils.write(responseBody, exchange.getResponseBody(), Charset.defaultCharset());
exchange.close();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment