ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
12.49k stars 1.02k forks source link

server-test-host: Rethrown exception in StatusPages exception handler fails test #1701

Open juggernaut0 opened 4 years ago

juggernaut0 commented 4 years ago

Ktor Version and Engine Used (client or server and name) Using ktor server 1.3.1, with ktor-server-test-host

Describe the bug When using the StatusPages feature to handle exceptions, a rethrowing the exception will bubble out to the test and cause a failure even if it was successfully handled with a call.respond. Rethrowing exceptions is suggested as a way to log caught exceptions in the documentation: https://ktor.io/servers/features/status-pages.html#logging-exceptions

To Reproduce

    @Test
    fun test() {
        open class MyException : RuntimeException()

        withTestApplication({
            install(StatusPages) {
                exception<MyException> {
                    call.respond(HttpStatusCode.UnprocessableEntity, it.toString())
                    throw it // Comment out this line to pass test, but not log exception
                }
            }
            routing {
                get("/") {
                    throw MyException()
                }
            }
        }) {
            with(handleRequest {
                method = HttpMethod.Get
                uri = "/"
            }) {
                assertEquals(HttpStatusCode.UnprocessableEntity, response.status())
            }
        }
    }

Expected behavior I expect the above test to pass, and the exception to be logged as it would in a non-test application

cy6erGn0m commented 4 years ago

This is by design behaviour: uncaught exceptions are always treated as errors and should not occur.

juggernaut0 commented 4 years ago

Okay that makes sense. Perhaps a note should be added to the documentation about that.

cab404 commented 4 years ago

This is by design behaviour: uncaught exceptions are always treated as errors and should not occur.

As good as it sounds, that doesn't help us testing. Is there any way of handling those exceptions (probably in routing?), so we can test error messages?

For now I plan to add wrap each lambda I give to routing into try-catch, but that's obviously a hack.

e5l commented 4 years ago

Hi @cab404, to do that you can add the pipeline interceptor and wrap the proceed method with a try catch block.

RyanGrif commented 3 years ago

Just encountered this myself, and found that you can also get around it by just logging the exception manually, rather than re-throwing it:


    @Test
    fun test() {
        val logger = KotlinLogging.logger {}

        open class MyException : RuntimeException()

        withTestApplication({
            install(StatusPages) {
                exception<MyException> {
                    logger.error(it)
                    call.respond(HttpStatusCode.UnprocessableEntity, it.toString())
                }
            }
            routing {
                get("/") {
                    throw MyException()
                }
            }
        }) {
            with(handleRequest {
                method = HttpMethod.Get
                uri = "/"
            }) {
                assertEquals(HttpStatusCode.UnprocessableEntity, response.status())
            }
        }
    }