Hey guys, I'm trying to write a test w/ junit and ...
# ktor
a
Hey guys, I'm trying to write a test w/ junit and handleRequest to test my login endpoint. If I use postman or curl or etc to make the request, it works just fine, but with my test function every one of my debug prints is null and prints inside the route don't go off. Here's my test code
Copy code
@Test
    fun `can register user and get jwt`() = withApplication {
        main()
        val newUser = NewUser("someusername", "someemail", "Tr0ub4dor83")
        val req = handleRequest(<http://HttpMethod.Post|HttpMethod.Post>, "/api/users/register") {
            setBody(
                Gson().toJson(newUser)
            )
        }
        println(req.response.status().toString())
        println(req.response.headers)
        println(req.response.content)
        val token = req.response.headers["Authorization"]
        println(token)
    }
am I doing something wrong without realizing?
r
Have a closer look at the examples at the bottom of this page: https://ktor.io/servers/testing.html You need two lambdas, one where you call your
main
function, and the second one where you do your testing. Right now, you're doing both inside the testing lambda, which won't work properly. Without having tested it, your code should probably look closer to this:
Copy code
@Test
    fun `can register user and get jwt`() = withApplication({ main() }) {
        val newUser = NewUser("someusername", "someemail", "Tr0ub4dor83")
        val req = handleRequest(<http://HttpMethod.Post|HttpMethod.Post>, "/api/users/register") {
            setBody(
                Gson().toJson(newUser)
            )
        }
        println(req.response.status().toString())
        println(req.response.headers)
        println(req.response.content)
        val token = req.response.headers["Authorization"]
        println(token)
    }
a
Hey, I was asleep by the time you sent that, but thanks so much! I hate these kinds of problems, appreciate the fix.
r
Timezones 🎉 You're welcome!
a
Hey, I actually just got back to work on my kotlin project and tried to implement your solution, but there's still an error that I don't know how to fix. Here's my error,

http://shekels.wtf/i/P5etiGC.pngâ–¾

Here's my code (99% sure it's verbatim what you sent)
Copy code
@Test
    fun `can register user and get jwt`() = withApplication({ main() }) {
        val newUser = NewUser("someusername", "someemail", "Tr0ub4dor83")
        val req = handleRequest(<http://HttpMethod.Post|HttpMethod.Post>, "/api/users/register") {
            setBody(
                Gson().toJson(newUser)
            )
        }
        println(req.response.status().toString())
        println(req.response.headers)
        println(req.response.content)
        val token = req.response.headers["Authorization"]
        println(token)
    }
more info

http://shekels.wtf/i/PxODe11.pngâ–¾

r
Oh, I see, your main function is an actual main function, I was expecting it to be an extension on
Application
. And that's what it has to be for the
withApplication
function to work. So you'll probably have an
embeddedServer { ... }
-call in your main function, correct? If so, you need to factor the lambda body you're passing to that function out into its on function that's defined like this:
fun Application.mainModule() { /* lambda body goes here */ }
, then call it from the main function like that:
embeddedServer { mainModule() }
. This
mainModule
function you can now use in the test:
fun test() = withTestApplication({ mainModule()}) { ... }
a
oh
wait so
I do have a main function
Copy code
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Application.module() {
    install(DefaultHeaders)
    install(CallLogging)
    install(Locations)
    install(ContentNegotiation) {
        gson {
            setPrettyPrinting()
            serializeNulls()
            setDateFormat(DateFormat.FULL)
        }
    }
    install(Authentication) {
        jwt("jwt") {
            realm = config.realm
            verifier(authenticationService.verifier)
            validate { credential ->
                credential.payload.getClaim("id").asLong()?.let { id ->
                    transaction {
                        User.findById(id)
                    }
                }
            }
        }
    }


    install(Routing) {
        get("/a") {
            call.respondText("d", ContentType.Text.Html)
        }
        user(userService, authenticationService)
    }
}

@KtorExperimentalLocationsAPI
fun main() {
    userService.hi()
    embeddedServer(
        Netty,
        8080,
        module = Application::module
    ).start()
}
my main file looks like this
is this not already what you were saying? or did I just confuse myself
r
Oh yeah, that's perfect! Just do this then and you should be good:
Copy code
@Test
    fun `can register user and get jwt`() = withApplication(Application::module) {
        val newUser = NewUser("someusername", "someemail", "Tr0ub4dor83")
        val req = handleRequest(<http://HttpMethod.Post|HttpMethod.Post>, "/api/users/register") {
            setBody(
                Gson().toJson(newUser)
            )
        }
        println(req.response.status().toString())
        println(req.response.headers)
        println(req.response.content)
        val token = req.response.headers["Authorization"]
        println(token)
    }
The important thing is that
withTestApplication
does not actually start your server like your main function does, but instead starts a dummy server that does no actual HTTP processing and applies your configuration (your module-function) to it. Makes for faster testing without having to start the whole server 🙂
a

http://shekels.wtf/i/gPEKHjk.pngâ–¾

still a type mismatch
;d
r
Oh, yeah, that needs to be
withTestApplication
, not
withApplication
🙈
a
perfect! that did it! thanks so much