如何在 KMM 上为 SQLDelight 编写单元测试
Posted
技术标签:
【中文标题】如何在 KMM 上为 SQLDelight 编写单元测试【英文标题】:How to Write Unit Tests for SQLDelight on KMM 【发布时间】:2021-04-16 04:38:51 【问题描述】:我想知道如何在 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 驱动程序依赖项的正确方法吗?
任何帮助将不胜感激!
【问题讨论】:
【参考方案1】:您可以在KaMPKit 中看到一个基本示例。
如果您在非测试代码中配置了 sqldelight,则不需要在 commonTest
中对它自己的驱动程序依赖。
在我们的测试代码中,我们有一个 expect
来创建用于测试的数据库连接。
internal expect fun testDbConnection(): SqlDriver
然后在ios 和android 代码中,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 (test) 中没有实际声明”。在 KaMPKit 项目中,我没有找到任何与 JVM 相关的内容。【参考方案2】:我在使用 Context 进行测试时遇到问题,发现使用内存数据库更快。这还具有不需要设备进行测试的好处。
我的做法:
-
将 JdbcSqliteDriver 添加到 androidTest 源集(“共享”下的 build.gradle)
val androidTest by getting
dependencies
// ...
implementation("com.squareup.sqldelight:sqlite-driver:1.4.4")
-
添加期望/实际函数来创建驱动程序:
internal expect fun createTestSqlDriver(): SqlDriver
internal actual fun createTestSqlDriver(): SqlDriver
return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply
MyDatabase.Schema.create(this)
-
现在我可以在“commonTest”下的测试中创建和使用数据库。比如:
internal class MyClassDbTests
private val sqlDriver = createTestSqlDriver()
private val myDatabase = 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)
我发现以下帖子很有用:
SQLDelight 1.x Quick Start Guide for Android SQLDelight Testing【讨论】:
用这个IN_MEMORY测试更快【参考方案3】:感谢您的回答!我遇到了另一个问题。 “预期的函数‘createDriver’在 JVM 的模块 KMM.shared (test) 中没有实际声明”。在 KaMPKit 项目中,我没有找到任何与 JVM 相关的内容。
Getting Started on JVM with SQLite 包含必要的说明。
你需要添加一个依赖
dependencies
implementation "com.squareup.sqldelight:sqlite-driver:1.5.0"
进入你的“jvmMain”sourceSet,接下来在你的“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")
【讨论】:
【参考方案4】:例子
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", "2", "Click,Close")
assertThat(queries.selectAllEvents().executeAsList())
.containsExactly(
Event(
as_counter = "1",
t_ms = "2",
event_map = "Click,Open"
),
Event(
as_counter = "2",
t_ms = "2",
event_map = "Click,Close"
)
)
@Test
fun `delete multiple events`()
queries.insertEvent("1", "1", "Click,Open")
queries.insertEvent("1", "2", "Click,Close,Read,Open")
queries.insertEvent("1", "3", "Click,Open")
queries.insertEvent("2", "3", "Click,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",
event_map = "Click,Open"
), Event(
as_counter = "2",
t_ms = "3",
event_map = "Click,Open"
),
)
@Test
fun `delete single event`()
queries.insertEvent("1", "1", "Click,Open")
queries.insertEvent("1", "2", "Click,Close,Read,Open")
queries.insertEvent("1", "3", "Click,Open")
queries.insertEvent("2", "3", "Click,Open")
queries.deleteEventListByKey("1", "3")
assertThat(queries.selectAllEvents().executeAsList())
.containsExactly(
Event(
as_counter = "1",
t_ms = "1",
event_map = "Click,Open"
), Event(
as_counter = "2",
t_ms = "3",
event_map = "Click,Open"
),
Event(
as_counter = "1",
t_ms = "2",
event_map = "Click,Close,Read,Open"
)
)
@Test
fun `update events`()
queries.insertEvent("1", "2", "Click,Open")
queries.insertEvent("1", "2", "Click,Close")
assertThat(queries.selectAllEvents().executeAsList())
.containsExactly(
Event(
as_counter = "1",
t_ms = "2",
event_map = "Click,Close"
)
)
参考资料 https://github.com/touchlab/KaMPKit/blob/main/shared/src/commonTest/kotlin/co/touchlab/kampkit/SqlDelightTest.kt
【讨论】:
以上是关于如何在 KMM 上为 SQLDelight 编写单元测试的主要内容,如果未能解决你的问题,请参考以下文章
KMM: sqldelight:coroutines-extensions 将 kotlinx-coroutines-core 版本设置为 1.3.9