I need to run unit test on my DAOs in ktor, so I'm using an H2 in-memory database, but the data seems to persist between test of the same class.
class TestUserDao : KoinTest {
private lateinit var database: Db
private val sut: UserDao by inject()
@BeforeEach
fun setUp() {
database = get<Db>(named("test"))
}
@Test
fun allUsers() {
runBlocking {
val users = sut.allUsers()
assertEquals(1, users. Size) // when running the test individually, this fails
}
}
@Test
fun addNewUser() {
val fullName = "Test User"
runBlocking {
val user = sut.addNewUser(
fullName = fullName
)
assertEquals(fullName, user.fullName)
}
}
companion object {
@JvmStatic
@BeforeAll
fun `start koin`(): Unit {
startKoin {
modules(dbModule)
}
}
}
}
I have tried changing the injected Db
from singleton
to factory
but doesn't seem to change anything.
Also, removing the ;DB_CLOSE_DELAY=-1
causes the tests to fail completely with errors saying the required tables have not yet been created
// di modules
val dbModule = module {
factory(qualifier = named("test")) {
Db(
driverClassName = "org.h2.Driver", jdbcURL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"
)
}
}
// db.kt
internal class Db(driverClassName: String, jdbcURL: String) {
private val database: Database = Database.connect(jdbcURL, driverClassName)
init {
transaction(database) {
addLogger(StdOutSqlLogger)
SchemaUtils.create(UsersTable, Locations)
}
}
companion object {
suspend fun <T> dbQuery(block: suspend () -> T): T =
newSuspendedTransaction(Dispatchers.IO) { block() }
}
}
I ended up having to generate and pass random names to the database for tests
class TestUserDao : KoinTest {
private lateinit var database: Db
private var currentTest = Random.nextInt()
@BeforeEach
fun setUp() {
database = get<Db>(named("test")) {
parametersOf("test${currentTest}")
}
}
}
and the koin module becomes
factory(qualifier = named("test")) { params ->
Db(
driverClassName = "org.h2.Driver",
jdbcURL = "jdbc:h2:mem:${params.get<String>()};DB_CLOSE_DELAY=-1"
)
}
i believe there's a more elegant way, but this solved my issue