You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为所有Ktor测试复用相同的测试服务器配置?

问题分析

你遇到的核心问题是**testApplication内的配置代码未被执行**,直接跳到了测试lambda逻辑,导致Dagger注入未触发,lateinit变量未初始化。主要原因是:

  • testApplication是Ktor提供的挂起函数,但你的testApp函数未声明为suspend,且测试方法未正确处理挂起上下文,导致testApplication内部逻辑被跳过。
  • JUnit默认不直接支持挂起测试方法,需要配合协程测试工具来执行挂起逻辑。
解决方案

1. 修改testApp为挂起函数

首先将BaseTest中的testApp函数声明为suspend,确保它能正确传递挂起上下文:

protected suspend fun testApp(block: suspend () -> Unit) = testApplication {
    environment {
        config = MapApplicationConfig().apply {
            put("ktor.deployment.test", "true")
            put("ktor.deployment.port", "8080")
            put("ktor.deployment.mongo_url", "mongodb://localhost")
            put("ktor.deployment.server_environment", "development")
        }
    }

    myClient = createClient {
        install(ContentNegotiation) {
            jackson {
                defaultConfig()
            }
        }
    }

    application {
        appInjector = DaggerTestApplicationComponent
            .builder()
            .environment(environment)
            .configurationModule(ConfigurationModule(environment))
            .loggingModule(LoggingModule(environment))
            .build()

        (appInjector as TestApplicationComponent).inject(this@BaseTest)

        configureRouting()
        configureSecurity()
        configureSerialization()
    }

    block() // 直接调用挂起函数,等价于invoke()
}

2. 让测试方法支持挂起逻辑

使用kotlinx-coroutines-test的runTest包裹测试逻辑,或者将测试方法声明为suspend(需JUnit 5支持):

方式一:用runTest包裹(推荐)

修改测试类的测试方法:

class GetUsersControllerTest : BaseTest() {

    companion object {
        const val ROUTE = "/api/v1/users"
    }

    @Test
    fun `Successfully receive list of users of page of users`() = runTest {
        testApp {
            // Add users
            for (i in 0 .. 30) {
                mongoQueries.insertUser(MongoUser(i, randomString(10), randomString(10)))
            }

            val response = myClient.get(ROUTE) {
                contentType(ContentType.Application.Json)
            }

            assertEquals(HttpStatusCode.OK, response.status)

            val list = response.body<List<MongoUser>>()

            assertEquals(BasePagedResultController.DEFAULT_PAGE_SIZE, list.size)
        }
    }
}

需要确保依赖中包含kotlinx-coroutines-test:

testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")

方式二:声明测试方法为suspend

如果使用JUnit 5,也可以直接将测试方法声明为suspend,但需要确保项目配置了支持挂起测试的扩展:

@Test
suspend fun `Successfully receive list of users of page of users`() = testApp {
    // ... 测试逻辑不变
}

3. 验证Dagger注入逻辑

确保你的TestApplicationComponent正确声明了注入BaseTest的方法:

@Component(modules = [ConfigurationModule::class, LoggingModule::class, ...])
interface TestApplicationComponent : ApplicationComponent {
    // 必须声明此方法,才能注入BaseTest实例
    fun inject(baseTest: BaseTest)
}

额外优化:避免重复创建客户端

可以将myClient的初始化移到application块内,或者确保每次测试都重新初始化,避免测试间的状态污染:

// 在testApplication内的application块后初始化客户端
myClient = createClient {
    install(ContentNegotiation) {
        jackson {
            defaultConfig()
        }
    }
    // 可以添加测试专用配置,比如禁用重试等
}
验证步骤
  1. 运行测试,检查mongoQueries和myClient是否被正确初始化。
  2. 如果仍有问题,可以在application块内添加日志输出,确认注入代码是否执行:
application {
    println("Initializing Dagger component for test")
    appInjector = DaggerTestApplicationComponent...
    (appInjector as TestApplicationComponent).inject(this@BaseTest)
    println("Injected dependencies to BaseTest: ${mongoQueries != null}")
    // ... 其他配置
}

内容的提问来源于stack exchange,提问作者Vinnie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 03:55:38