As can be seen in our previous blogpost, getting started with Javalin and Gradle is quite straightforward. In my experience it’s good to focus on integration testing early in the project. This allows you to refactor with confidence, get quick feedback, and prevent bugs. When using Javalin, this is no different. This blogpost will dive into a way to easily create integrations tests. We’ll use the latest version of Javalin, Kotlin and Junit 5, plus we’ll use Fuel HTTP to test our endpoint.



What is Fuel HTTP



Fuel HTTP is one of the easiest networking libraries for Kotlin and quite actively developed. Fuel HTTP provides great support for handling standard HTTP calls out of the box. There are also modules which support other frameworks such as GSON, Jackson, RxJava and many more. This makes Fuel HTTP well suited for testing REST endpoints, which is what we’ll test in this blogpost.



Our application overview



The application used in this blogpost is a very small one which just retrieves persons by invoking an HTTP GET call with an id parameters to a person endpoint, such as /api/person/1 , where 1 is the id of the person. The result of this will be a JSON structure representing the person, as can be seen below:



{"name":"Yvonne","age":29}

The application builds upon the Getting started with Javalin application in our previous blogpost. If you haven’t read it, now might be a good idea do that. Note that you won’t need any code from the previous blogpost though.



The total solution consists of the following:



The Gradle build file to manage our dependencies

The application configuration including the routing

The person domain, including a controller, a DTO and a repository

The actual integration tests

The gradle build file



// build.gradle.kt import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { val kotlinVersion = "1.3.21" id("org.jetbrains.kotlin.jvm") version kotlinVersion application } tasks.withType<Test> { useJUnitPlatform() } dependencies { api("io.javalin:javalin:2.8.0") implementation("org.slf4j:slf4j-simple:1.7.26") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8") testImplementation("org.junit.jupiter:junit-jupiter:5.4.1") testImplementation("com.github.kittinunf.fuel:fuel:2.0.1") testImplementation("com.github.kittinunf.fuel:fuel-jackson:2.0.1") } repositories { jcenter() } application { // Define the main class for the application mainClassName = "ApplicationKt" }

As you can see in the Kotlin Gradle build file above, we’ve defined our production and test dependencies. Some of the dependencies we want to expose, which are defined as api dependencies. The implementation details are hidden from anyone having a dependency on our application. This is done by using the implementation dependencies. Furthermore, we also have our Fuel and JUnit 5 test as testImplementation dependencies, which are used to execute our integration tests.

Application configuration and routing



// Application.kt import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.get import io.javalin.apibuilder.ApiBuilder.path import person.PersonController import person.personRepository fun main() { JavalinApp(7000).init() } class JavalinApp(private val port: Int) { fun init(): Javalin { val app = Javalin.create().apply { port(port) exception(Exception::class.java) { e, _ -> e.printStackTrace() } }.start() val personController = PersonController(personRepository) app.get("/api/person/:id", personController::getPerson) return app } }

In the above, we’ve defined our JavalinApp. It’s extracted to a separate class to make it easier to invoke it from the test. The Javalin class is responsible for managing the running application, which includes setting up the controller and the API routes.

Person domain model



// person.Person.kt package person import io.javalin.Context class PersonController(private val data: Map<Int, Person>) { fun getPerson(ctx: Context) { ctx.pathParam("id").toInt().let { data[it]?.let { item -> ctx.json(item) return } ctx.status(404) } } } data class Person(val name: String, val age: Int) // In-memory repository val personRepository = hashMapOf( 0 to Person("Dmitry", 37), 1 to Person("Yvonne", 29), 2 to Person("Peter", 52) )

The above code has been merged together into one file. This isn’t necessarily a great idea, but for the purpose of our blogpost, this was easier. It contains a controller which is using our injected repository, a person class, and the in memory repository itself containing our dummy persons.

Integration tests

// PersonIntegrationTest.kt package person import JavalinApp import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.fuel.httpGet import com.github.kittinunf.fuel.jackson.responseObject import io.javalin.Javalin import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertEquals @TestInstance(TestInstance.Lifecycle.PER_CLASS) @DisplayName("Person API") class PersonIntegrationTest { private lateinit var app: Javalin @BeforeAll fun setUp() { app = JavalinApp(8000).init() // Inject the base path to no have repeat the whole URL FuelManager.instance.basePath = "http://localhost:${app.port()}/" } @AfterAll fun tearDown() { // Stops the application when the tests have completed app.stop() } @Test fun `should get result for existing person`() { // Deconstructs the ResponseResult val (_, _, result) = "api/person/0".httpGet().responseObject<Person>() // Get the actual value from the result object assertEquals(personRepository[0], result.get()) } @Test fun `should get error for non-existing person`() { val (_, _, result) = "api/person/-1".httpGet().responseObject<Person>() // Deconstructs the ResponseResult to get the FuelError val (_, error) = result assertEquals(404, error!!.response.statusCode) } }

And now, after setting up our production infrastructure, this is our end result. It’s an integration test which is testing two scenarios: a scenario in which there is a result found and a scenario which results into an error.

In the above code, we’re using the synchronous version of Fuel HTTP, which is blocking until there is a result. Then we destructure the result into a separate fields and extract the ones we need.

Note: if anyone know how to get rid of the !! in an elegant way, please leave a comment below.

Executing the test using gradlew test or using your favorite IDE will result in the following test report:

Person API +-- should get error for non-existing person [PASSED] +-- should get result for existing person [PASSED]

While the example above is quite limited and in no way covers all the aspects of REST endpoint, it hopefully sets you up to start testing Javalin applications. If you have any comments, please leave me a message below!