Grzes
Grzes

Reputation: 971

sending request to the newly created jetty server for testing purposes

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.

Upvotes: 2

Views: 3428

Answers (2)

McGivrer
McGivrer

Reputation: 15

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 !

Upvotes: 0

Joakim Erdfelt
Joakim Erdfelt

Reputation: 49462

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.

Upvotes: 5

Related Questions