【问题标题】:Junit assertions don't work when called inside a Javalin handler在 Javalin 处理程序中调用 Junit 断言时不起作用
【发布时间】:2019-11-20 09:39:13
【问题描述】:

我有这个测试在端口7000 启动服务,并且在唯一的端点内我做了一个失败的断言:

@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()
    )
}

问题是测试总是通过(但它应该失败):

(运行./gradlew test会发生同样的行为)

...即使有一个控制台输出声称测试失败:

[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)
    ..

可能它正在另一个线程中运行,但我想知道是否有办法将它附加到相同的上下文中。 (奇怪的是,在我的应用程序的另一个场景中 - 我无法隔离 - 它正确地失败了。)

【问题讨论】:

    标签: kotlin junit junit5 javalin


    【解决方案1】:

    TLDR

    要使测试如您预期的那样失败,请在您从 Javalin 实例获得的响应上添加一个断言。

        @Test
        fun `javalin assertion should fail`() {
            Javalin.create()
                .get("/") { assertTrue(false) } // or any expression that throws an Exception, like Kotlin's TODO()
                .start()
    
            val javalinResponse: HttpResponse<Void> = newHttpClient().send(
                HttpRequest.newBuilder()
                    .uri(URI.create("http://localhost:7000/"))
                    .GET().build(),
                discarding()
            )
    
            assertThat(javalinResponse.statusCode()).isEqualTo(200) // will fail with Expected: 200, Actual: 500
        }
    

    详情

    测试中有两个不同的步骤:新的 Javalin 实例配置 + 构建,以及使用 HttpClient 调用该实例。

    在 Javalin 配置 + 构建步骤中,当在 / 端点上调用时,它会被指示执行 assertTrue(false)assertTrue(false) 将抛出 AssertionFailedError,但如果你在那里抛出其他东西,行为将是相同的。现在,与许多(所有?)其他网络服务器一样,Javalin / Jetty 将尝试捕获其中发生的任何未捕获的异常并返回带有代码 500(内部服务器错误)的 HTTP 响应。

    确实,这一切都发生在另一个线程中,因为内部启动了一个 Jetty 网络服务器实例,它负责端口监听、HTTP 请求/响应处理和其他重要的事情。

    因此,当稍后在测试中对新的 Javalin 实例执行 HTTP 调用时,它会成功获得 500(内部服务器错误)响应,并且最初响应中没有断言没有未捕获的异常,则认为测试成功。

    【讨论】:

    【解决方案2】:

    永远不要在 Javalin 处理程序中进行断言,因为如果测试失败,JUnit 异常会被 Javalin 吞下,并且测试会静默失败 (better explained in the other answer)。解决方案是在最后进行断言,如 Arrange, Act, Assert 模式。

    怎么样?您将要声明的内容存储在处理程序中并稍后声明。例如,如果它是 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
    )
    

    如果是 GET,那就更简单了:

    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
    )
    

    更多信息Unit testing a gateway with Javalin

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-30
      • 2017-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多