I'm writing integration JUnit test. My task is to test whether the response of my local server is correct. The mentioned server takes as a GET parameter an address of page to be analysed (for example: localhost:8000/test?url=http://www.example.com).
To avoid being dependent on www.example.com I want to start for this particular test my own jetty server, which always serves the same content.
private static class MockPageHandler extends AbstractHandler {
public void handle(String target,Request baseRequest, HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
final String responseString = loadResource("index.html");
response.getWriter().write(responseString);
baseRequest.setHandled(true);
}
}
public void test() throws Exception {
final int PORT = 8080;
final Server server = new Server(PORT);
server.setHandler(new MockPageHandler());
server.start();
final ContentResponse response =
client.newRequest("http://localhost:8000/test?url=http://localhost:8080").send();
/* some assertions. */
server.stop();
server.join();
}
Every time I execute this test, the handle method in MockPageHandler is never invoked.
Do you have any suggestions why this not works?
P.S. When I remove server.stop() and in browser type http://localhost:8080 the proper page is shown.
Quick answer:
Remove the server.join() line. That line makes the junit thread wait until the server thread stops. Which is not needed for unit testing.
Long answer:
What we (the jetty developers) have learned about using jetty embedded servers with junit.
Use the #Before and #After annotations to start and stop the server if you have 1 test method, or some requirement that the server be pristine between test methods.
Example #Before / #After (Jetty 9.x):
public class MyTest
{
private Server server;
private URI serverUri;
#Before
public void startServer() throws Exception
{
this.server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(0); // let connector pick an unused port #
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
context.addServlet(new ServletHolder(new MyServlet()),"/my/*");
// Start Server
server.start();
String host = connector.getHost();
if (host == null)
{
host = "localhost";
}
int port = connector.getLocalPort();
this.serverUri = new URI(String.format("http://%s:%d/",host,port));
}
#After
public void stopServer()
{
try
{
server.stop();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
#Test
public void testMe()
{
// Issue request to server
URI requestUri = serverUri.resolve("/my/test");
// assert the response
}
}
This technique makes the server start on port 0, which is a magic number that tells the underlying stack to pick an empty port and start listening. The test case then asks the server what port number it is listening on and builds out the serverUri field to be appropropriate for this test run.
This technique works great, however, it will start/stop the server for each method.
Enter, the better technique, use the #BeforeClass and #AfterClass annotations to start/stop the server once for the entire test class, running all of the methods inside of the test class against this started server.
Example #BeforeClass / #AfterClass (Jetty 9.x):
public class MyTest
{
private static Server server;
private static URI serverUri;
#BeforeClass
public static void startServer() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(0); // let connector pick an unused port #
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
context.addServlet(new ServletHolder(new MyServlet()),"/my/*");
// Start Server
server.start();
String host = connector.getHost();
if (host == null)
{
host = "localhost";
}
int port = connector.getLocalPort();
serverUri = new URI(String.format("http://%s:%d/",host,port));
}
#AfterClass
public static void stopServer()
{
try
{
server.stop();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
#Test
public void testMe()
{
// Issue request to server
URI requestUri = serverUri.resolve("/my/test");
// assert the response
}
}
Doesn't look much different? Yes, the changes are subtle. #Before became #BeforeClass, #After became #AfterClass. The start/stop methods are now static. The server and serverUri fields are now static.
This technique is used where we have dozens of test methods that access the same server, and those requests do not alter the state in the server. This speeds up the test case execution by simply not recreating the server between each test method.
Give a try to "com.jayway.restassured" for your http test. too easy to write some test :
#Test
public void testNotGetAll() {
expect().
statusCode(404).
when().
get(baseUrl+"/games/");
}
this method call "http://mywebserver.local:8080/rest/games/" and verify that a 404 http status code is returned.
And this approach synchronised with a Jetty server (for example) started at pre-integration-test in the maven lifecycle, you match the perfect mix to process integration test !
Related
I have problems reading the parameters with the following URL:
http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600
Example from: https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3
I receive the request with simple implementation of com.sun.net.httpserver.HttpServer for integration test purposes.
Apparently, the HTTPServer can not handle the # symbol in the URL.
Here is my code. The System.out prints only: /oidc_test_callback
What do I need to do to read the parameter 'access_token'?
class OidcCallbackServer {
private final HttpServer server;
private final OidcCallbackHandler oidcCallbackHandler;
OidcCallbackServer(final int port) throws IOException {
this.server = HttpServer.create(new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), port), 0);
this.oidcCallbackHandler = new OidcCallbackHandler();
this.server.createContext("/oidc_test_callback", this.oidcCallbackHandler);
this.server.setExecutor(null); // creates a default executor
this.server.start();
}
private class OidcCallbackHandler implements HttpHandler {
#Override
public void handle(final HttpExchange request) throws IOException {
URI requestURI = request.getRequestURI();
System.out.println(requestURI);
}
}
Here is my URL for the Keycloak Server:
https://keycloak:8443/auth/realms/testRealm/protocol/openid-connect/auth?client_id=my_client_id&redirect_uri=http%3A%2F%2F192.168.202.105%3A50022%2Foidc_test_callback&response_type=id_token&scope=openid+profile&state=vZq7QdXKXsHQ3cF8hczQ4cUgPNjMjfqij-cgI7pIv4E&nonce=wHzXl08I49_OzYkA5lJkn0ZEitWZfJQFEoF12bMoK3A
...and it results in:
http://my-local-ip:50022/oidc_test_callback#state=vZq7QdXKXsHQ3cF8hczQ4cUgPNjMjfqij-cgI7pIv4E&session_state=d23b427b-99fa-4bcb-a939-b66a3e91d77d&id_token=---content-of-id-token---
When "response_type" in the first URL is "code" instead of "id_token" the URL looks like:
http://192.168.202.105:50022/oidc_test_callback?state=VgWjE0IxIc2JV3iZ14KzLsXGeBPtvKeJURnNL2yE9FA&session_state=d23b427b-99fa-4bcb-a939-b66a3e91d77d&code=4cc3a01a-a0d1-482c-8bb6-163a4d7fe287.d23b427b-99fa-4bcb-a939-b66a3e91d77d.f0001f26-ff85-47c6-befa-76f97a68ad02
SO there is no "#" symbol but the common "?".
I am trying to write a mini-library for testing to mock common external services such as E-mail, SFTP, Buckets, HTTP APIs.
At the moment, I got stuck on WireMockServer. In WireMock docs it states that I can create both server and client to verify API calls.
I wrote the class:
public class WireMockTestServer {
private final WireMockServer server;
public WireMockTestServer(String address, MappingBuilder mappingBuilder) {
server = new WireMockServer(wireMockConfig().dynamicPort().dynamicHttpsPort());
}
public WireMockTestServer(int httpPort, int httpsPort, String address, MappingBuilder mappingBuilder) {
server = setup(
new WireMockServer(wireMockConfig().port(httpPort).httpsPort(httpsPort).bindAddress(address)),
mappingBuilder
);
}
private WireMockServer setup(WireMockServer server, MappingBuilder mappingBuilder) {
server.stubFor(mappingBuilder);
return server;
}
public void start() {
server.start();
}
public void stop() {
server.stop();
}
}
which I can path endpoint declaration and redirect my services toward it.
When I am trying to test it:
public class WireMockTestServerTest {
#Test
public void testSetup() throws Exception {
MappingBuilder mappingBuilder = get(urlEqualTo("/health"))
.willReturn(aResponse().withHeader("Content-Type", "application/json")
.withStatus(200));
WireMockTestServer server = new WireMockTestServer(8888, 9988, "127.0.0.1", mappingBuilder);
server.start();
// This line should fail
verify(getRequestedFor(urlEqualTo("/health")).withHeader("Content-Type", equalTo("text/xml")));
server.stop();
}
}
The test fails. The issue is, it fails not because of an assertion but because it starts on a wrong port 8080 which is occupied by other processes.
How can I start WireMockServer on another port and test it with JUnit 5?
I am using Java 8, Maven, Spring Boot.
As mentioned in comment static verify method tries to verify against default wiremock instance. Since you are creating a standalone instance in your test you should verify against it. Create a verify method in your WireMockTestServer :
public void verify(final RequestPatternBuilder requestPatternBuilder) {
server.verify(requestPatternBuilder);
}
and then you can verify against it :
#Test
public void testSetup() throws Exception {
MappingBuilder mappingBuilder = get(urlEqualTo("/health"))
.willReturn(aResponse().withHeader("Content-Type", "application/json")
.withStatus(200));
WireMockTestServer server = new WireMockTestServer(8888, 9988, "127.0.0.1", mappingBuilder);
server.start();
// This line should fail
server.verify(getRequestedFor(urlEqualTo("/health")).withHeader("Content-Type", equalTo("text/xml")));
server.stop();
}
I have this code for server:
Undertow server = Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(Handlers.path()
.addPrefixPath("/item", new ItemHandler())
)
.build();
server.start();
And handler:
private class ItemHandler implements HttpHandler {
#Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
exchange.getPathParameters(); // always null
//ItemModel item = new ItemModel(1);
//exchange.getResponseSender().send(mapper.writeValueAsString(item));
}
}
I want to send request /item/10 and get 10 in my parameter. How to specify path and get it?
You need a PathTemplateHandler and not a PathHandler, see:
Undertow server = Undertow.builder()
.addHttpListener(8080, "0.0.0.0")
.setHandler(Handlers.pathTemplate()
.add("/item/{itemId}", new ItemHandler())
)
.build();
server.start();
Then, in your ItemHandler:
class ItemHandler implements HttpHandler {
#Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
// Method 1
PathTemplateMatch pathMatch = exchange.getAttachment(PathTemplateMatch.ATTACHMENT_KEY);
String itemId1 = pathMatch.getParameters().get("itemId");
// Method 2
String itemId2 = exchange.getQueryParameters().get("itemId").getFirst();
}
}
The method 2 works because Undertow merges parameters in the path with the query parameters by default.
If you do not want this behavior, you can use instead:
Handlers.pathTemplate(false)
The same applies to the RoutingHandler, this is probably what you want to use eventually to handle multiple paths.
Handlers.rounting() or Handlers.routing(false)
I'm trying to embed Jetty 8 (8.1.18.v20150929) into a Java (jdk1.7.0_67) application. I have the following code:
public static final String HTTP_PATH = "/session";
public static final int HTTP_PORT = 9995;
// Open the HTTP server for listening to requests.
logger.info("Starting HTTP server, Port: " + HTTP_PORT + ", Path: "
+ "/session");
httpServer = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(HTTP_PORT);
connector.setHost("localhost");
httpServer.addConnector(connector);
TestHttpHandler handler = new TestHttpHandler(this);
ContextHandler ch = new ContextHandler();
ch.setContextPath(HTTP_PATH);
ch.setHandler(handler);
httpServer.setHandler(ch);
try {
httpServer.start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
My handler is pretty basic as a test:
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
logger.debug("Handling");
}
If I run the app and then use CURL to send a GET request to http://localhost:9995/session, then it returns a 200 status but there's no debug output.
If I access http://localhost:9995/session2, I get a 404 error.
I've read many examples online but for some reason I can't seem to get the handler to work properly. Am I doing something wrong? Thanks
I had exactly the same problem, and this is just a misunderstanding about how the Jetty API works. I was expecting to use ContextHandlers as a minimal implementation of REST services, however ContextHandlers are meant to handle requests to an entire context base (for example http://server:80/context-base, i.e. the equivalent of an app in Tomcat). The correct way to solve this question is to use Servlets:
Server server = new Server(9995);
ServletContextHandler root = new ServletContextHandler(ServletContextHandler.NO_SECURITY | ServletContextHandler.NO_SESSIONS);
root.setContextPath("/");
ServletHolder holder = new ServletHolder(new HttpServlet() {
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
logger.debug("Handling");
}
});
server.start();
server.join();
In a unit test using an Apache HttpClient to fire requests, I have seen the following setup and cleanup code:
private HttpClient httpClient;
private HttpRequestBase httpRequest;
#Before
public void setUp() throws Exception {
httpClient = new DefaultHttpClient();
}
#After
public void closeRequests() {
if (httpRequest != null) {
httpRequest.releaseConnection();
httpRequest = null;
}
}
The tests than e.g. send get requests and check the response:
#Test
public void getSomething() throws Exception {
httpGet = new HttpGet("http://some/url");
HttpResponse response = httpclient.execute(httpGet);
assertThat(response.getStatusLine().getStatusCode(), is(HttpStatus.SC_OK));
}
Now my question is: Do these tests properly clean up after themselves? From what I understand, the releaseConnection() call only hands back the connections to the client's connection manager but doesn't actually close it.
So shouldn't the tests rather do this:
#After
public void closeConnections() {
httpClient.getConnectionManager().shutdown();
}
And would this properly close all connections even without calling releaseConnection() on the http request instances?
Yes, you should. Shutting down the connection manager (or closing the client with HC 4.3 and newer) is the right thing to do in integration tests.