DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Activate New User Accounts by Email
  • Spring Reactive Microservices: A Showcase
  • Why Camel K?
  • How to Develop Microservices With Spring Cloud and Netflix Discovery

Trending

  • The Human Side of Logs: What Unstructured Data Is Trying to Tell You
  • Testing SingleStore's MCP Server
  • Vibe Coding With GitHub Copilot: Optimizing API Performance in Fintech Microservices
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  1. DZone
  2. Coding
  3. Frameworks
  4. Getting Started With Javalin

Getting Started With Javalin

In this post, we’ll create a small hello world application with Javalin based on Gradle Kotlin and then test of our application.

By 
Erik Pragt user avatar
Erik Pragt
·
Apr. 18, 19 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
9.9K Views

Join the DZone community and get the full member experience.

Join For Free

With the popularity of Spring Boot and the great support for integrating with different types of frameworks, it’s often hard to imagine using something different than Spring. However, there are numerous frameworks out there which provide similar features for building applications like microservices, and Javalin is one of those frameworks. This post will help you to get started with Javalin.

What Is Javalin?

Javalin is a web framework for building Java and Kotlin web applications in a similar fashion to frameworks like Node.js (see Koajs.com) or SparkJava, which is not surprising, since the author is one of the original SparkJava authors.

Creating a Gradle Build for Javalin

In this post, we’ll create a small hello world application based on Gradle Kotlin, including the testing of our application.

To get started with Javalin, we need to have a Javalin dependency. An easy way to do that is to use Gradle. Assuming you have Gradle installed, we can create a new project with the following command:

gradle init --type java-library --dsl kotlin

(Answer the prompted questions, their values aren’t really important for the purpose of this guide.)

Once the project has been initialized, open the build.gradle.kts file, and replace the contents with the following content:

// build.gradle.kts
plugins {
    val kotlinVersion = "1.3.21"
    id("org.jetbrains.kotlin.jvm") version kotlinVersion
    application
}

application {
    // Define the main class for the application and add Kt for Kotlin classes
    mainClassName = "HelloWorldKt"
}

dependencies {
    compile("io.javalin:javalin:2.8.0")
    compile("org.slf4j:slf4j-simple:1.7.26")

    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
}

repositories {
    jcenter()
}

The main library used here is the Javalin 2.8.0 library, which at the time of writing this piece is the latest one. After this, navigate to src/main/kotlin, and create a HelloWorld.kt file. This file will contain the contents of our complete application.

// src/main/kotlin/HelloWorld.kt
import io.javalin.Javalin

fun main() {
    val app = Javalin.create().start(7000)
    app.get("/") { ctx -> ctx.result("Hello World") }
}

Now, running gradlew run from a terminal will start up the Javalin application, running on port 7000. Opening http://localhost:7000 in your browser will result in the root route being called (app.get("/")), which will display the Hello World text in your browser. Now you’re ready to include more functionality into your app and to write a test for it, which I’ll dive into in the next section.

Testing Javalin

After creating our initial application, we will dive into a way to easily create integrations tests. We'll use the latest version of 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 post.

Our Application Overview

We'll enhance our existing application we wrote before. We'll adapt it so it retrieves persons by invoking an HTTP GET call with an id parameter 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 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 changed the build file to include our production and test dependencies. Some of the dependencies we want to expose 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 include 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 Javalin app. 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, inspired by one of the Javalin tutorials, has been merged together into one file. This isn't necessarily a great idea, but for the purpose of our post, 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 separate fields and extract the ones we need.

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!

application integration test Spring Framework Dependency

Published at DZone with permission of Erik Pragt. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Activate New User Accounts by Email
  • Spring Reactive Microservices: A Showcase
  • Why Camel K?
  • How to Develop Microservices With Spring Cloud and Netflix Discovery

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!