Building a Kotlin Dropwizard Maven REST API from scratch: Part 3

Part 3: Testing

Before we run anything, it is probably a good idea to know it’s going to behave the way we intended to. The best way to do this is to write test, we can do this using normal junit testing (I am sure you are familiar with from Java) and it’s the same in Kotlin. In the src folder, create this folder structure; test -> kotlin -> com -> mannanlive -> dropwizardkotlinapp -> resources. Inside your newly created resources folder, create a new file EchoResourceTest.kt with the below contents.

package com.mannanlive.dropwizardkotlinapp.resources

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectWriter
import org.junit.Assert
import org.junit.Test

class EchoResourceTest {
    val mapper: ObjectWriter = ObjectMapper().writerWithDefaultPrettyPrinter()

    @Test
    fun `echo resource prints json correctly`() {
        val echoResource = EchoResource("unit-test")

        val response = echoResource.get("query")

        val json = mapper.writeValueAsString(response)

        Assert.assertEquals("""
            {
              "query" : "query",
              "prop" : "unit-test",
              "number" : 123,
              "bool" : false,
              "array" : [ 456, 789 ],
              "map" : {
                "rate" : 11.5
              },
              "date" : "31-01-2019 10:20:42"
            }
        """.trimIndent(), json)
    }
}

There is a couple of Kotlin techniques going on, so let me help try and break them down. The first cool language feature is the method names with spaces, using the back-tick (`). This is great for expressing descriptive test method names, but I wouldn’t recommend it for production code. The next feature is the multiline string, which is great for capturing quotes (“) and new lines (\n) which are both used in pretty formatted JSON. The trimIndent() removes all the indentation of the string so you can ignore the whitespace at the beginning of each line.

This is great to test that the code is behaving as expected on the class level, but how do we know it will all hang together, loading the configuration file correctly, what object mapper configuration is configured, what response status code will be returned and more. To do this, integration tests is a viable way of achieving this goal. A benefit of Dropwizard is that it’s lightning fast, especially compared to Spring Boot, so writing integration tests aren’t too expensive time wise. Complexity wise, they are not too tedious easier with the use of the dropwizard-testing framework and Kotlin. To use this, add this to your <dependencies> block in your pom.xml from Step 1.

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.dropwizard</groupId>
    <artifactId>dropwizard-testing</artifactId>
    <version>${dropwizard.version}</version>
    <scope>test</scope>
</dependency>

In the same src/test/kotlin/com/mannanlive/dropwizardkotlinapp/resources folder, create EchoResourceIntegrationTest.kt. We are first going to create a static class junit rule using the provided DropwizardAppRule, configured to use our configuration class (AppConfig), our application class (App) and our YAML file (app-config.yml).

Then in our new test method we are going to create a new HTTP client and fire off a GET request to our running application’s echo endpoint. Then we want to assert the status code is what we expect (200 OK) and the JSON is correct.

package com.mannanlive.dropwizardkotlinapp.resources

import com.mannanlive.dropwizardkotlinapp.App
import com.mannanlive.dropwizardkotlinapp.configuration.AppConfig
import io.dropwizard.testing.ResourceHelpers.resourceFilePath
import io.dropwizard.testing.junit.DropwizardAppRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule
import org.junit.Test
import javax.ws.rs.client.ClientBuilder

class EchoResourceIntegrationTest {
    companion object {
        @JvmField
        @ClassRule
        val RULE = DropwizardAppRule<AppConfig>(App::class.java,
                resourceFilePath("app-config.yml"))
    }

    @Test
    fun `can GET echo resource successfully`() {
        val response = ClientBuilder.newClient()
                .target("http://localhost:${RULE.localPort}/echo")
                .request()
                .get()!!

        assertThat(response.status).isEqualTo(200)
        assertThat(response.readEntity(String::class.java))
                .isEqualTo("{\"query\":\"default\"," +
                        "\"prop\":\"abc\"," +
                        "\"number\":123," +
                        "\"bool\":false," +
                        "\"array\":[456,789]," +
                        "\"map\":{\"rate\":11.5}," +
                        "\"date\":\"31-01-2019 10:20:42\"}")
    }
}

You might notice that two of these attributes have different values from when we ran the unit test. The first one is the query parameter is set to default and not query like it was in our unit test. In our unit test, you need to pass a non-null string into our get method in our resource. So there is no way using the unit test method to simulate the behaviour of a user requesting our /echo resources without passing any query parameters and ultimate falling back to our @DefaultValue(“default”) annotation. You can add .queryParam(“query”, “foo-bar”) after our .target(“…”) call if you want to simulate a user requesting a call like /echo?query=foo-bar

You might also have also noticed the JSON data is different in that it has “prop” property is not set to “unit-test” but instead set to “abc”, the same value in our app-config.yml. This demonstrates that our config file was loaded up correctly and injected into our EchoResource in our App.kt. You can try changing the value in the YAML file and see the what comes though in the test.

Finally, the JSON doesn’t look very nice and readable in this format, we could use the multi-line string (“””) again like we did in the unit test. However, many people don’t like JSON being in their tests for readability and maintainability both great reason! So let us introduce some test fixtures and use them instead. Firstly, you need to make a folder structure that matches /src/test/resources/fixtures and in the fixtures folder, create a file named echo.json

{
  "query" : "foo-bar",
  "prop" : "abc",
  "number" : 123,
  "bool" : false,
  "array" : [ 456, 789 ],
  "map" : {
    "rate" : 11.5
  },
  "date" : "31-01-2019 10:20:42"
}

Then, back in our integration test, change the line that tested the JSON payload: assertThat(response.readEntity(… to the below.

assertThat(response.readEntity(String::class.java))
        .isEqualToIgnoringWhitespace(fixture("fixtures/echo.json"))

Now isn’t that more readable? There is a lot more that can be done with testing, but at least now we are fairly sure that our new echo Dropwizard Kotlin resource is ready to be built and released out into the world.

About the Author

Mannan

Mannan is a software engineering enthusiast and has been madly coding since 2002. When he isn't coding he loves to travel, so you will find both of these topics on this blog.

Leave a Reply

Your email address will not be published. Required fields are marked *