HTTP server from scratch: Unit testing

This is a part two in the series to build a web server from scratch without external dependencies. Part one can be found here

Now that we have minimal setup working, next area of focus is testing. If we were to follow Test Driven Development (TDD), test cases would have to be written first before any working code. But it is never too late to write some test cases.

When it comes to testing streams like in this case, the easy way is using ByteArrayInputStream and ByteArrayOutputStream. Feed it a byte array or get back a byte array and verify. To help with the unit testing we will be using JUnit and Mockito. I know these are external dependencies, but these are development only dependencies and does not affect the production output.

As a start we will cover one success case, serving the index.html, and two failure cases, 404 and 501.

Test setup

In the setup method we will initialize the mock for Socket to mimic an incoming request. Depending on the test case we will configure the mock to return a different stream of bytes. To help us with this we will create two helper methods generateIncomingRequest and prepareIncomingRequestStream.

private String generateIncomingRequest(String method, String resource) {
    return method + " " + resource + " HTTP/1.1\n" +
            "Host: localhost:8080\n" +
            "User-Agent: curl/7.61.1\n" +
            "Accept: */*\n";
}

private void prepareIncomingRequestStream(String stream) throws IOException {
    InputStream inputStream = new ByteArrayInputStream(stream.getBytes());
    when(client.getInputStream()).thenReturn(inputStream);
}

200 – Success case

The incoming request is for / or index, to which the HttpServer is expected to return index.html. Expected output HTTP/1.1 200 OK.

@Test
public void request_index_success() throws IOException {
    // setup the request
    prepareIncomingRequestStream(generateIncomingRequest("GET", "/"));

    // test
    httpRequestHandler.run();

    // verify
    StringTokenizer tokenizer = new StringTokenizer(new String(outputStream.toByteArray()));
    assertEquals("HTTP/1.1", tokenizer.nextToken());
    assertEquals("200", tokenizer.nextToken());
}

404 – Not found

Generally web browsers will also make a request to /favicon.ico, in our case for this we are expected to return 404 Not Found.

@Test
public void request_otherResource_notFound() throws IOException {
    // setup
    prepareIncomingRequestStream(generateIncomingRequest("GET", "/favicon.ico"));

    // test
    httpRequestHandler.run();

    // verify
    StringTokenizer tokenizer = new StringTokenizer(new String(outputStream.toByteArray()));
    assertEquals("HTTP/1.1", tokenizer.nextToken());
    assertEquals("404", tokenizer.nextToken());
}

501 – Not Implemented

Our web server only supports GET, any other request method should fail. Since it is not supported, we will return 501 Not Implemented as a response.

@Test
public void request_post_noImplemented() throws IOException {
    // setup
    prepareIncomingRequestStream(generateIncomingRequest("POST", "/"));

    // test
    httpRequestHandler.run();

    //verify
    StringTokenizer tokenizer = new StringTokenizer(new String(outputStream.toByteArray()));
    assertEquals("HTTP/1.1", tokenizer.nextToken());
    assertEquals("501", tokenizer.nextToken());
}

These test cases will give us good test coverage to start. The complete code at this point in time can be found on the bare-bones-2 branch of the repository.

One thought on “HTTP server from scratch: Unit testing

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s