如何在 KMM 上为 SQLDelight 编写单元测试

问题描述

我想知道如何在 KMM 上为 sqlDelight 编写单元测试。首先,我什至无法正确添加 sqlDelight 依赖项。

    val commonTest by getting {
        dependencies {
            implementation(kotlin("test-common"))
            implementation(kotlin("test-annotations-common"))
            // sqlDelight tests
            implementation("com.squareup.sqldelight:sqlite-driver:1.4.3")
        }
    }

在我添加依赖项然后同步项目后,该项目甚至没有构建。有人可以告诉我这是否是添加 sqlite 驱动程序依赖项的正确方法

任何帮助将不胜感激!

解决方法

您可以在 KaMPKit 中看到一个基本示例。

如果您在非测试代码中配置了 sqldelight,则不需要在 commonTest 中依赖它自己的驱动程序。

在我们的测试代码中,我们有一个 expect 为测试创建数据库连接。

internal expect fun testDbConnection(): SqlDriver

然后在 iOSAndroid 代码中,actual 定义。

依赖配置看起来(大致)是这样的:

commonMain {
  implementation("com.squareup.sqldelight:runtime:1.4.4")
}

androidMain {
 implementation("com.squareup.sqldelight:android-driver:1.4.4")
}

iosMain {
  implementation("com.squareup.sqldelight:native-driver:1.4.4")
}

有了这个,您应该能够编写 sqldelight 测试。

,

感谢您的回答!我遇到了另一个问题。 “预期的函数‘createDriver’在 JVM 模块 KMM.shared(测试)中没有实际声明”。在 KaMPKit 项目中,我没有找到任何与 JVM 相关的内容。

Getting Started on JVM with SQLite 包含必要的说明。

你需要添加一个依赖

dependencies {
  implementation "com.squareup.sqldelight:sqlite-driver:1.5.0"
}

进入你的“jvmMain”源集,接下来在你的“jvmMain”模块中实现真正有趣的createDriver。


我感谢 Kevin 的回答,并补充说使用 SqlDeLite 的测试应该放在平台模块(“androidTest”和“iosTest”)中,而不是放在“commonTest”中。

您需要向您的 SUT 提供实际驱动程序的实现,使用应用上下文。 对于单元测试,您需要替换上下文,例如查看 Robolectric

添加依赖

dependencies {
    implementation("org.robolectric:robolectric:4.4")
}

进入“androidTest”sourceSet(不知道iOS可以用什么),获取应用上下文:

val context = ApplicationProvider.getApplicationContext<Context>()

并使用它来获取驱动程序的平台实现:

val driver = DatabaseDriverFactory(context).createDriver(Database.Schema,"test.db")
,

我在使用 Context 进行测试时遇到问题,发现使用内存数据库会更快。这样做的另一个好处是不需要用于测试的设备。

我的做法:

  1. 将 JdbcSqliteDriver 添加到 androidTest 源集(“共享”下的 build.gradle)
val androidTest by getting {
    dependencies {
        // ...
        implementation("com.squareup.sqldelight:sqlite-driver:1.4.4")
    }
}
  1. 添加 expect/actual 函数来创建驱动程序:
  • 在“commonTest”目录下的文件中(例如createTestSqlDriver.kt)

internal expect fun createTestSqlDriver(): SqlDriver

  • 在“androidTest”下的文件中
internal actual fun createTestSqlDriver(): SqlDriver {
    return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply {
        MyDatabase.Schema.create(this)
    }
}
  1. 现在我可以在“commonTest”下的测试中创建和使用数据库。类似于:
internal class MyClassDbTests {

    private val sqlDriver = createTestSqlDriver()
    private val calendarDb = MyDatabase(sqlDriver)

    fun insert_addItems_verifyCorrectNumOfItemsInDb() {
        // GIVEN
        val myQueries = myDatabase.mydbQueries
        myQueries.deleteAllEvents()
        
        val numItemsBeforeInsertion = myQueries.selectAll().executeAsList().size

        // WHEN
        myQueries.insertItem(1,2,3)
        myQueries.insertItem(10,20,30)

        val numItemsAfterInsertion = myQueries.selectAll().executeAsList().size

        // THEN
        assertEquals(0,numItemsBeforeInsertion)
        assertEquals(2,numItemsAfterInsertion)
    }
}

我发现以下帖子很有用:

,

示例

package com.viki.vikilitics_kmm

import com.squareup.sqldelight.sqlite.driver.JdbcDriver
import com.google.common.truth.Truth.assertThat
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
import com.viki.vikiliticskmm.Event
import com.viki.vikiliticskmm.EventQueries
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.sql.DriverManager
import java.sql.Connection


class AndroidEventDatabaseTest {
    private lateinit var queries: EventQueries

    // When your test needs a driver
    @Before
    fun before() {
        val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)

        val database = EventDatabase(driver)

        EventDatabase.Schema.create(driver)
        queries = database.eventQueries
    }


    @Test
    fun `select all events`() {
        queries.insertEvent("1","2","{Click,Open}")
        queries.insertEvent("2",Close}")

        assertThat(queries.selectAllEvents().executeAsList())
            .containsExactly(
                Event(
                    as_counter = "1",t_ms = "2",event_map = "{Click,Open}"
                ),Event(
                    as_counter = "2",Close}"
                )
            )
    }

    @Test
    fun `delete multiple events`() {

        queries.insertEvent("1","1",Open}")
        queries.insertEvent("1",Close},{Read,"3",Open}")

        val event1 = listOf("1","3")
        val event2 = listOf("1","2")
        val event3 = listOf("1","4")
        val eventList = listOf(event1,event2,event3)
        for (event in eventList){
            queries.deleteEventListByKey(event.elementAt(0),event.elementAt(1))
        }

        assertThat(queries.selectAllEvents().executeAsList())
            .containsExactly(
                Event(
                    as_counter = "1",t_ms = "1",t_ms = "3",)

    }

    @Test
    fun `delete single event`() {

        queries.insertEvent("1",Open}")
        queries.deleteEventListByKey("1","3")

        assertThat(queries.selectAllEvents().executeAsList())
            .containsExactly(
                Event(
                    as_counter = "1",Event(
                    as_counter = "1",Open}"
                )
            )

    }

    @Test
    fun `update events`() {

        queries.insertEvent("1",Close}")
        assertThat(queries.selectAllEvents().executeAsList())
            .containsExactly(
                Event(
                    as_counter = "1",Close}"
                )
            )


    }

}

参考资料 https://github.com/touchlab/KaMPKit/blob/main/shared/src/commonTest/kotlin/co/touchlab/kampkit/SqlDelightTest.kt