Hello Team, Newbie to Kotlin and Ktor. I need hel...
# ktor
r
Hello Team, Newbie to Kotlin and Ktor. I need help in adding unit tests which uses a testing database. Is there a reference that I can follow. I'm using Ktor
2.0.2
. Basically I need to understand the setup(connection to test db and create data for testing) and teardown(cleanup of test data after all tests) parts using the
testApplication
Thanks in advance!
a
You can use the
TestApplicationEngine
class and setup/tear down functionality of a test framework to solve your problem. Here is an example with MongoDB:
Copy code
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoDatabase
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.testing.*
import io.ktor.test.dispatcher.*
import org.junit.After
import org.junit.AfterClass
import org.junit.BeforeClass
import org.litote.kmongo.KMongo
import org.litote.kmongo.getCollection
import kotlin.test.Test
import kotlin.test.assertEquals

class DBTest {
    companion object {
        private lateinit var server: TestApplicationEngine
        private lateinit var mongoClient: MongoClient
        private lateinit var db: MongoDatabase
        private val httpClient: HttpClient get() = server.client

        @BeforeClass
        @JvmStatic
        fun setup() {
            mongoClient = KMongo.createClient()
            db = mongoClient.getDatabase("test")

            server = TestApplicationEngine(createTestEnvironment {
                developmentMode = false // to prevent a bug with a class loader in Ktor 2.0.2
                module {
                    routes(db)
                }
            })

            server.start(wait = false)
        }

        @AfterClass
        @JvmStatic
        fun tearDown() {
            server.stop()
            mongoClient.close()
        }
    }

    @After
    fun dropCollection() {
        db.getCollection<Name>().drop()
    }

    @Test
    fun checkSomeNames() = testSuspend {
        db.getCollection<Name>().insertMany(
            listOf(
                Name("John"),
                Name("Mike"),
                Name("Eddie"),
            )
        )

        assertEquals("John,Mike,Eddie", httpClient.get("/").bodyAsText())
    }

    @Test
    fun checkNoNames() = testSuspend {
        assertEquals("", httpClient.get("/").bodyAsText())
    }
}

fun Application.routes(db: MongoDatabase) {
    routing {
        get("/") {
            val collection = db.getCollection<Name>()
            val names = collection.find().toList()

            call.respondText {
                names.joinToString(separator = ",") { it.value }
            }
        }
    }
}

data class Name(val value: String)
r
Thanks @Aleksei Tirman [JB]. I'll cheeck this out
Appreciate the help 🙂
Hi @Aleksei Tirman [JB], Got the tests working with connection to test_database. But for the UserTests to pass, we need ApplicationTest to be called first which is expected but this might not work with tests running in parallel.
Copy code
BaseTest.kt
-----------

object BaseTest {
  private lateinit var server: TestApplicationEngine
  lateinit var db: Database
  val httpClient: HttpClient get() = server.client

  fun setup() {
    server = TestApplicationEngine(createTestEnvironment {
      developmentMode = false // to prevent a bug with a class loader in Ktor 2.0.2
      module {
        configureRouting()
        configureSerialization()
        db = DbFactory.init()
      }
    })

    server.start(wait = false)
  }
}

ApplicationTest.kt
-------------------

class ApplicationTest {
  @Test
  fun init() {
    BaseTest.setup()
  }
}


UserTest.kt
------------

class UserTest {
  companion object {
    @AfterClass
    @JvmStatic
    fun purgeData() {
      transaction(DbTest.db) { UserTable.deleteWhere { UserTable.id eq 4 }}
    }
  }

  @Test
  fun testRoot() = testSuspend() {
    val response = <http://DbTest.httpClient.post|DbTest.httpClient.post>("/api/v1/users") {
      contentType(ContentType.Application.Json)
      setBody(
        Gson().toJson(mockUser())
      )
    }

    assertEquals(HttpStatusCode.Created, response.status)
  }

  private fun mockUser(): UserCreateModel {
    return UserCreateModel(
      "4",
      "Ram",
      "<mailto:ram619prasad@sam.com|ram619prasad@sam.com>",
      "testPassword",
      "7382119086"
    )
  }
}
Is there a proper way to have the dbconnection, creation of test httpClient and etc so that the same can be re-used by multiple test files rather than ensuring our ApplicationTest runs first and then later the rest of the many test files.