如何为所有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() } } // 可以添加测试专用配置,比如禁用重试等 }
验证步骤
- 运行测试,检查
mongoQueries和myClient是否被正确初始化。 - 如果仍有问题,可以在
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
相关产品推荐
相关产品推荐

