So i have built my Ktor backend and im watching a tutorial from philip lackner Tutorial He starts of with making a withTestApplication call within his testfunction... The thing is that this has been deprecated and it would be nice to use the newer version of it which i understand is
@Test
fun `Create user, no body attached, responds with BadRequest`() = testApplication{
application {
install(Routing) {
createUserRoute(userRepository)
}
}
}
And so i use this code but i have no clue how to get the scope so i can use handleRequest? please can somebody help me with this?
This is the code i wanted to test
package com.PapperSeller.routes
import com.PapperSeller.controller.user.UserRepository
import com.PapperSeller.data.models.User
import com.PapperSeller.data.requests.CreateAccountRequest
import com.PapperSeller.data.responses.BasicApiResponse
import com.PapperSeller.util.ApiResponseMessages
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.koin.ktor.ext.inject
fun Route.createUserRoute(repository: UserRepository){
route("/api/user/create"){
post {
kotlin.runCatching { call.receive<CreateAccountRequest>()}.onSuccess {request ->
val userExist = repository.getUserByEmail(request.email) != null
if(userExist){
call.respond(BasicApiResponse(ApiResponseMessages.USER_ALREADY_EXIST, false))
return@post
}else{
if(request.email.isBlank() || request.username.isBlank() || request.password.isBlank()){
call.respond(BasicApiResponse(ApiResponseMessages.FIELDS_EMPTY, false))
return@post
}else{
repository.createUser(User(email = request.email, username = request.username, password = request.password))
call.respond(BasicApiResponse("", true))
return@post
}
}
}.onFailure {
call.respond(BasicApiResponse(ApiResponseMessages.NO_USER_REGISTERED, false))
return@post
}
}
}
}
Here is an example of how you can cover the Route.createUserRoute
with tests:
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.testing.*
import kotlinx.serialization.Serializable
import kotlin.test.Test
import kotlin.test.assertEquals
class KtorTest {
@Test
fun userNotRegistered() = testApplication {
application {
module(MemoryUserRepository())
}
val client = createClient {
install(ContentNegotiation) {
json()
}
}
assertEquals(
BasicApiResponse(ApiResponseMessages.NO_USER_REGISTERED, false),
client.post("/api/user/create").body()
)
}
@Test
fun userAlreadyExist() = testApplication {
application {
module(MemoryUserRepository(mapOf("john@doe" to User("", "", ""))))
}
val client = createClient {
install(ContentNegotiation) {
json()
}
}
client.post("/api/user/create") {
setBody(CreateAccountRequest("john@doe", "", ""))
contentType(ContentType.Application.Json)
}.body<BasicApiResponse>().let { response ->
assertEquals(BasicApiResponse(ApiResponseMessages.USER_ALREADY_EXIST, false), response)
}
}
@Test
fun emptyRequestFields() = testApplication {
application {
module(MemoryUserRepository())
}
val client = createClient {
install(ContentNegotiation) {
json()
}
}
client.post("/api/user/create") {
setBody(CreateAccountRequest("", "", ""))
contentType(ContentType.Application.Json)
}.body<BasicApiResponse>().let { response ->
assertEquals(BasicApiResponse(ApiResponseMessages.FIELDS_EMPTY, false), response)
}
}
@Test
fun successfulRegistration() = testApplication {
application {
module(MemoryUserRepository())
}
val client = createClient {
install(ContentNegotiation) {
json()
}
}
client.post("/api/user/create") {
setBody(CreateAccountRequest("john@doe", "john", "123"))
contentType(ContentType.Application.Json)
}.body<BasicApiResponse>().let { response ->
assertEquals(BasicApiResponse("", true), response)
}
}
}
fun Application.module(repository: UserRepository) {
install(io.ktor.server.plugins.contentnegotiation.ContentNegotiation) {
json()
}
routing {
createUserRoute(repository)
}
}
fun Route.createUserRoute(repository: UserRepository) {
route("/api/user/create") {
post {
kotlin.runCatching { call.receive<CreateAccountRequest>() }.onSuccess { request ->
val userExist = repository.getUserByEmail(request.email) != null
if (userExist) {
call.respond(BasicApiResponse(ApiResponseMessages.USER_ALREADY_EXIST, false))
return@post
} else {
if (request.email.isBlank() || request.username.isBlank() || request.password.isBlank()) {
call.respond(BasicApiResponse(ApiResponseMessages.FIELDS_EMPTY, false))
return@post
} else {
repository.createUser(
User(
email = request.email,
username = request.username,
password = request.password
)
)
call.respond(BasicApiResponse("", true))
return@post
}
}
}.onFailure {
call.respond(BasicApiResponse(ApiResponseMessages.NO_USER_REGISTERED, false))
return@post
}
}
}
}
class ApiResponseMessages {
companion object {
val USER_ALREADY_EXIST = "User does already exist"
val FIELDS_EMPTY = "Something missing"
val NO_USER_REGISTERED = "User isn't registered"
}
}
interface UserRepository {
fun getUserByEmail(email: String): User?
fun createUser(user: User)
}
class MemoryUserRepository(initialUsers: Map<String, User> = mapOf()): UserRepository {
private val users = initialUsers.toMutableMap()
override fun getUserByEmail(email: String): User? {
return users[email]
}
override fun createUser(user: User) {
users[user.email] = user
}
}
@Serializable
data class CreateAccountRequest(val email: String, val username: String, val password: String)
data class User(val email: String, val username: String, val password: String)
@Serializable
data class BasicApiResponse(val message: String, val success: Boolean)