kotlinktorkoinkotlin-exposed

in-memory database is persisted between test of same class in ktor


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() }
    }
}

Solution

  • 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