kotlinjunitjunit5javalin

Junit assertions don't work when called inside a Javalin handler


I have this test that launches a service at port 7000 and inside the only endpoint I do a failing assertion:

@Test
fun `javalin assertion should fail`() {
    Javalin.create()
            .get("/") { assertTrue(false) }
            .start()

    newHttpClient().send(
            HttpRequest.newBuilder()
                    .uri(URI.create("http://localhost:7000/"))
                    .GET().build(),
            discarding()
    )
}

The problem is that the test always passes (but it should fail):

enter image description here

(same behavior happens by running ./gradlew test)

... even though there's a console output claiming that a test failed:

[Test worker] INFO io.javalin.Javalin - Listening on http://localhost:7000/
[Test worker] INFO io.javalin.Javalin - Javalin started in 356ms \o/
[qtp2100106358-22] ERROR io.javalin.Javalin - Exception occurred while servicing http-request
org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
    at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
    ..

Probably, it's being run in another thread, but I wonder if there's a way to attach it to the same context. (Weirdly, in another scenario in my app - that I couldn't isolate - it properly fails.)


Solution

  • Never do assertions inside the Javalin handler, because if the test fails, the JUnit exception is swallowed by Javalin and the test fails silently (better explained in the other answer). The solution is to make an assertion outside, in the end, as in the Arrange, Act, Assert pattern.

    How? You store what you want to assert inside the handler and assert it later. For example, if it's a POST.

    var postedBody: String? = null
    fakeProfileApi = Javalin.create().post("profile") {
        postedBody = it.body()
    }.start(1234)
    val profileGateway = ProfileGateway(apiUrl = "http://localhost:1234")
    
    profileGateway.saveProfile(  // contains the HTTP POST
       Profile(id = "abc", email = "john.doe@gmail.com".toEmail())
    )
    
    JSONAssert.assertEquals(
        """ { "id": "abc", "email": "johndoe@gmail.com" } """,
        postedBody, true
    )
    

    If it's a GET, it's easier:

    fakeProfileApi = Javalin.create().get("profile/abc") {
       it.result(""" {"id": "abc", "email": "johndoe@gmail.com"} """)
    }.start(1234)
    val profileGateway = ProfileGateway(apiUrl = "http://localhost:1234")
    
    val result = profileGateway.fetchProfile("abc") // contains the HTTP GET
    
    assertEquals(
       Profile(id = "abc", email = "john.doe@gmail.com".toEmail()),
       result
    )
    

    More info: Unit testing a gateway with Javalin