Unit testing Java Spark microframework
Asked Answered
S

2

6

I am trying to add a restful api to a java microservice. For this, I am using spark:

http://sparkjava.com/documentation.html

I've created a very simple class which stands up an api. That class is here:

public class Routes {
    public void establishRoutes(){
        get("/test", (req, res) -> "Hello World");
        after((req, res) -> {
            res.type("application/json");
        });

        exception(IllegalArgumentException.class, (e, req, res) -> {
            res.status(400);
        });
    }

Now, running Routes.establishRoutes() should stand up an api which would show "Hello World" in the event someone decides to visit http://localhost:4567/test. This does actually work. Hurray!

The next step is unit testing the code. My unit test, unfortunately, does not succeed. The spark documentation does not detail a sound way for doing testing so what I have is pieced together from examples I found around the net. Here is my Junit test:

public class TestRoutes {
        @Before
        public void setUp() throws Exception {
            Routes newRoutes = new Routes();
            newRoutes.establishRoutes();
        }

        @After
        public void tearDown() throws Exception {
            stop();
        }

        @Test
        public void testModelObjectsPOST(){

            String testUrl = "/test";


            ApiTestUtils.TestResponse res = ApiTestUtils.request("GET", testUrl, null);
            Map<String, String> json = res.json();
            assertEquals(201, res.status);
        }

Here is the code behind ApiTestUtils.request():

public class ApiTestUtils {
    public static TestResponse request(String method, String path, String requestBody) {


        try {
            URL url = new URL("http://localhost:4567" + path);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod(method);
            connection.setDoOutput(true);
            connection.connect();
            String body = IOUtils.toString(connection.getInputStream());
            return new TestResponse(connection.getResponseCode(), body);
        } catch (IOException e) {
            e.printStackTrace();
            fail("Sending request failed: " + e.getMessage());
            return null;
        }
    }

    public static class TestResponse {

        public final String body;
        public final int status;

        public TestResponse(int status, String body) {
            this.status = status;
            this.body = body;
        }

        public Map<String,String> json() {
            return new Gson().fromJson(body, HashMap.class);
        }
    }
}

I am failing on connection.connect() inside ApiTestUtils.request(). Specifically, I get the error: java.lang.AssertionError: Sending request failed: Connection refused

I believe this is happening because the application isn't listening when my test tries to make the request. However, I don't understand why that would be the case. I borrowed the test code from the demo project found here: https://github.com/mscharhag/blog-examples/blob/master/sparkdemo/src/test/java/com/mscharhag/sparkdemo/UserControllerIntegrationTest.java

UPDATE: I tried running the example linked above. Turns out, it doesn't work either. Looks like spinning up a spark instance in this context is more difficult than I thought? I'm not trying to figure out how to do so.

Sow answered 14/10, 2016 at 6:16 Comment(4)
You should read up on the difference between unit testing and integration testing. What you are trying to do with that code is the latter. A unit test would for example call establishRoutes directly in the Test method and verify the return value or the side effect.Bates
Fair enough. Do you feel that junit is an inappropriate place for integration tests?Sow
Junit is ok for that. You have to make sure you start your server though. It doesn't like you are doing that anywhere. Look in the example the use of of MainBates
This is what I thought as well. However, the Main method in the example doesn't appear to be doing anything I'm not doing in Routes.establishRoutes(). Do you observe a method call or action that is being taken which I am failing to take?Sow
H
11

In your test case is missing the code used for waiting the initialization of the embedded server. I've tried your code and stumbled on the same issue as you did, but after debugging it I've noticed that the embedded spark server is initialized in a newly created thread. ( see the method spark.Service#init()). All you need to do in your test is to await for the initialization by calling the method spark.Spark#awaitInitialization()

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static junit.framework.TestCase.assertEquals;
import static spark.Spark.awaitInitialization;
import static spark.Spark.stop;

public class TestRoutes {
    @Before
    public void setUp() throws Exception {
        Routes newRoutes = new Routes();
        newRoutes.establishRoutes();

        awaitInitialization();

    }

    @After
    public void tearDown() throws Exception {
        stop();
    }

    @Test
    public void testModelObjectsPOST() {

        String testUrl = "/test";


        ApiTestUtils.TestResponse res = ApiTestUtils.request("GET", testUrl, null);
        assertEquals(200, res.status);
    }

}
Hymenopterous answered 28/10, 2016 at 12:43 Comment(0)
Q
-4

I can't help you with Spark, but if you're open to trying an alternative lightweight Java library for writing micro services, have a look at Molecule.

You'll find documentation and examples for writing unit tests and integration tests. If I understand what you're trying to test correctly, the test would look like:

HttpRequest request = new HttpRequest(4567);
HttpResponse response = request.get("/test");
assertThat(response).hasStatusCode(201)
                    .hasContentType("application/json")
                    .hasBodyText("Hello World");
Quita answered 24/10, 2016 at 14:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.