如何Mock Kotlin SupabaseClient进行单元测试?
问题解决方案
一、单元测试修复
你遇到的错误是因为supabaseClient.postgrest是扩展属性,底层依赖supabaseClient.plugin(Postgrest)获取插件实例,直接Mock普通属性的方式会触发真实的扩展逻辑,导致检查插件是否存在的断言失败。
正确的Mockk配置步骤
- Mock所有需要的Supabase相关实例
- 替换插件获取逻辑(替代扩展属性的调用)
- Mock表访问和insert方法,捕获传入的参数用于断言
示例代码:
import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.postgrest.Postgrest import io.github.jan.supabase.postgrest.PostgrestBuilder import io.github.jan.supabase.postgrest.PostgrestResult import io.mockk.* import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class GitHubTrafficStoreTest { private val supabaseClient = mockk<SupabaseClient>() private val postgrest = mockk<Postgrest>() private val postgrestBuilder = mockk<PostgrestBuilder>() private val postgrestResult = mockk<PostgrestResult>() private val insertValuesSlot = slot<List<Clone>>() // 替换为你实际的Clone类型 @BeforeEach fun setUp() { // 1. Mock插件获取,替代postgrest扩展属性的逻辑 every { supabaseClient.plugin(Postgrest) } returns postgrest // 2. Mock表访问:访问"clone"表返回对应的Builder every { postgrest["clone"] } returns postgrestBuilder // 3. Mock insert调用,捕获传入的values参数 coEvery { postgrestBuilder.insert( values = capture(insertValuesSlot), upsert = true, onConflict = "owner, repository, timestamp" ) } returns postgrestResult // 4. Mock decodeList返回预期结果 coEvery { postgrestResult.decodeList<ResultClone>() } returns emptyList() } @Test fun `saveClones converts and inserts correct data`() = runTest { // 准备测试数据 val testOwner = "test-owner" val testRepo = "test-repo" val testClones = GitHubClones( clones = listOf(GitHubClone(count = 5, uniques = 3, timestamp = "2024-01-01T00:00:00Z")) ) // 调用测试方法 val result = yourStoreInstance.saveClones(testOwner, testRepo, testClones) // 断言传入insert的数据符合预期 val insertedData = insertValuesSlot.captured assert(insertedData.size == 1) with(insertedData.first()) { assert(owner == testOwner) assert(repository == testRepo) assert(count == 5) assert(uniques == 3) assert(timestamp == "2024-01-01T00:00:00Z") } // 断言返回结果符合预期 assert(result.isEmpty()) } }
二、集成测试方案
Supabase可以用Testcontainers进行集成测试,核心是利用Postgres的Testcontainer(因为Supabase本质是增强版Postgres),步骤如下:
1. 添加Testcontainers依赖
在你的构建文件中添加Postgres Testcontainer依赖:
// build.gradle.kts testImplementation("org.testcontainers:postgresql:1.19.8") testImplementation("org.testcontainers:junit-jupiter:1.19.8")
2. 编写集成测试
启动Postgres容器,初始化Supabase所需的表结构,创建真实的SupabaseClient连接,验证数据持久化逻辑:
import io.github.jan.supabase.createSupabaseClient import io.github.jan.supabase.postgrest.Postgrest import kotlinx.coroutines.runTest import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.testcontainers.containers.PostgresContainer import org.testcontainers.junit.jupiter.Container import org.testcontainers.junit.jupiter.Testcontainers @Testcontainers class GitHubTrafficStoreIntegrationTest { companion object { @Container val postgresContainer = PostgresContainer("postgres:15-alpine") .withDatabaseName("supabase_test") .withUsername("postgres") .withPassword("postgres") private lateinit var supabaseClient: SupabaseClient @BeforeAll @JvmStatic fun setUp() { // 创建连接到Testcontainer的SupabaseClient supabaseClient = createSupabaseClient( supabaseUrl = postgresContainer.jdbcUrl.replace("jdbc:postgresql:", "http://"), supabaseKey = "test-key" ) { install(Postgrest) } // 初始化clone表结构 runTest { supabaseClient.postgrest["clone"].execute( """ CREATE TABLE clone ( owner TEXT, repository TEXT, count INTEGER, uniques INTEGER, timestamp TEXT, PRIMARY KEY (owner, repository, timestamp) ) """.trimIndent() ) } } } @Test fun `saveClones persists data correctly`() = runTest { val store = YourGitHubTrafficStore(supabaseClient) val testOwner = "test-owner" val testRepo = "test-repo" val testClones = GitHubClones( clones = listOf(GitHubClone(count = 5, uniques = 3, timestamp = "2024-01-01T00:00:00Z")) ) // 调用保存方法 store.saveClones(testOwner, testRepo, testClones) // 查询数据库验证数据 val savedData = supabaseClient.postgrest["clone"] .select() .eq("owner", testOwner) .eq("repository", testRepo) .decodeList<Clone>() assert(savedData.size == 1) with(savedData.first()) { assert(count == 5) assert(uniques == 3) assert(timestamp == "2024-01-01T00:00:00Z") } } }
内容的提问来源于stack exchange,提问作者jlengrand
相关产品推荐
相关产品推荐

