I have some ktor server tests and ktor's server te...
# kotest
b
I have some ktor server tests and ktor's server test harness involves executing the requests inside of a scope, which means my verification of the responses happens there. Looks something like this:
Copy code
context("when the server") {
    withTestApplication({
        myServerModule()
    }) {
        context("receives a request for url/to/something") {
            with(handleRequest(<http://HttpMethod.Post|HttpMethod.Post>, "url/to/som ething") {
                should("return the correct response") {
                    response.status() shouldBe HttpStatusCode.OK
                }
            }
        }
    }
}
This doesn't work though, as where
should
is there isn't a coroutine scope and it complains. Is there a better way to do tests like this? Or would it be possible to have a non-suspend version of
should
?
I can wrap it
runBlocking
in the meantime, but that feels a bit hacky
s
are you on 4.1.1 ?
In 4.1.x we made all scopes be suspendable
b
4.1.1, yeah
s
seems like maybe one was missed
does it work if you change should to test, and ShouldSpec to FunSpec ?
b
Well, I think the scope is suspendable...at some layer...but I'm wrapped inside this ktor test harness scope
s
Actually I see
The with is changing the receiver probably, so you can't call should
try to reference the context lambda in a var. It might be ugly though
Copy code
context("when the server") {
    withTestApplication({
        myServerModule()
    }) {
        context("receives a request for url/to/something") { context ->
            with(handleRequest(<http://HttpMethod.Post|HttpMethod.Post>, "url/to/som ething") {
                context.should("return the correct response") {
                    response.status() shouldBe HttpStatusCode.OK
                }
            }
        }
    }
}
l
An alternative (which you probably thought of, but just in case) is to invert
with
and
should
1
And having ktor context as the last inner context
b
Thanks @sam that's not too bad
@LeoColman I've got multiple should blocks for the request, so can't flip unfortunately 😕
l
Yeah, that's what I thought
I believe KTor also has a way to execute that code without using
with
You can probably put ktor's receiver in a variable and operate with that inside your next scopes
b
Actually that's a good idea @LeoColman! That might be best.
Even just saving the return of
handleRequest
rather than using
with
Ah, shit...it's not that part which is screwing it up
(Or, at least, not only that part)
It's the top level server part
the
withTestApplication
lambda is a receiver
s
Do the same trick and move the context -> up to the top scope
b
Oh, wait, there is a non-suspending
should
?
Oh, but it doesn't take a TestContext block
s
don't mix up should scope with the should assertion
b
Yeah I'm looking at
ShouldSpecContextScope.kt
there are 4 shoulds there, but yeah I guess the non-suspend ones are for something else
I'm also considering writing a non-suspend
should
function there which just calls the suspend one inside of
runBlocking
Ok, I think I'm gonna do that ☝️ @sam is it PR-worthy to add that?
s
The framework shouldn't be calling runBlocking at all
b
ok
s
Copy code
fun should(name: String) =
      TestWithConfigBuilder(TestName("should ", name), testContext, defaultConfig, xdisabled = false)

   fun xshould(name: String) =
      TestWithConfigBuilder(TestName("should ", name), testContext, defaultConfig, xdisabled = true)
b
maybe
coroutineScope
would do it
s
Those two are non suspend because the test is passed into the second call on those
there are for
should("name").config() { }
b
"into the second call"
what do you mean?
ah
does
ContainerScope#addTest
need to be suspend?
Nevermind, the real question is if
registerTestCase
needs to be suspend
s
Yes, because it ends up calling into the spec runners
b
Ok
s
and that's getting in the heart of the execution of the tests, which are all coroutine based
b
Hm, naming the context receiver doesn't actually work since it's a receiver, not an argument.
And naming it via
context("some test") test@ {
and then doing
this@test.should
doesn't work either
s
inside the context, then do
val context = this
b
That still doesn't work, because it isn't within a coroutine body where I'm trying to call it
Hmph 😕
s
Copy code
context("when the server") {
    val c1 = this
    withTestApplication({
        myServerModule()
    }) {
        c1.context("receives a request for url/to/something") {
            with(handleRequest(<http://HttpMethod.Post|HttpMethod.Post>, "url/to/som ething") {
                should("return the correct response") {
                    response.status() shouldBe HttpStatusCode.OK
                }
            }
        }
    }
}
b
Ayyyyy. 🎉 Good idea
Oh, shit.
Same problem,
context
has to be called within a coroutine 😕
s
wrap it in a runblocking then
that's ok for your test case
b
Yeah