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

Ktor应用测试中Mockk无法替换RabbitService致RabbitMQ连接失败

解决Ktor应用测试中Mockk模拟RabbitService不生效的问题

问题核心

你的Ktor应用启动时硬编码创建了RabbitService实例并尝试连接RabbitMQ,测试中创建的Mockk对象和应用内部的实例完全独立,导致mock无法拦截真实的连接行为,最终出现Connection refused错误。

解决方案:使用依赖注入(DI)管理RabbitService

要让Mockk生效,必须让应用从外部容器获取RabbitService实例,而非硬编码创建。这里以Ktor常用的Koin DI框架为例:

步骤1:添加Koin依赖

在项目依赖配置中加入Ktor和测试相关的Koin依赖:

dependencies {
    // Koin核心依赖
    implementation("io.insert-koin:koin-ktor:3.4.0")
    // Koin测试依赖
    testImplementation("io.insert-koin:koin-test-junit5:3.4.0")
    // 保留原有的Mockk依赖
    testImplementation("io.mockk:mockk:1.13.5")
}

步骤2:重构应用代码,使用Koin注入

修改Application.kt,将RabbitService的创建逻辑移到Koin模块中,让应用从DI容器获取实例:

package some_app.processor

import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.response.respondText
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import org.koin.core.module.Module
import org.koin.dsl.module
import org.koin.ktor.plugin.Koin
import org.koin.ktor.plugin.get
import ru.noisedetector.processor.clients.RabbitService
import ru.noisedetector.processor.props.CustomApplicationProperties

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

// 定义Koin模块,负责提供RabbitService实例
val appModule: Module = module {
    single {
        val app = get<Application>()
        val appProps = CustomApplicationProperties(app.environment.config)
        RabbitService(
            host = appProps.getString("rabbitProps.host"),
            port = appProps.getInt("rabbitProps.port"),
            username = appProps.getString("rabbitProps.username"),
            password = appProps.getString("rabbitProps.password"),
            queueName = appProps.getString("rabbitProps.queue"),
        )
    }
}

@Suppress("unused")
fun Application.module() {
    // 安装Koin插件,加载应用模块
    install(Koin) {
        modules(appModule)
    }

    // 从Koin容器获取RabbitService实例
    val rabbitService = get<RabbitService>()
    rabbitService.defaultExchangeAndQueue()
    rabbitService.consumeQueue()

    routing {
        get("/") {
            call.respondText("Hello, world!")
        }

        get("/json/jackson") {
            call.respond(mapOf("hello" to "world"))
        }
    }
}

步骤3:修改测试代码,替换为Mock实例

在测试中,通过Koin加载测试模块,将RabbitService替换为Mockk创建的模拟对象:

import com.rabbitmq.client.ConnectionFactory
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.server.config.ApplicationConfig
import io.ktor.server.testing.testApplication
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.koin.core.module.Module
import org.koin.dsl.module
import org.koin.ktor.plugin.Koin
import ru.noisedetector.processor.clients.RabbitService
import ru.noisedetector.processor.module
import kotlin.test.Test
import kotlin.test.assertEquals

class ApplicationTest {

    @Test
    fun testDevEnvironment() = testApplication {
        application {
            // 安装Koin并加载测试模块,替换真实的RabbitService
            install(Koin) {
                modules(testModule)
            }
            module()
        }
        environment {
            config = ApplicationConfig("application.conf")
        }

        val resp = client.get("/")
        assertEquals("Hello, world!", resp.bodyAsText())
        // 验证mock方法被调用
        verify { testRabbitService.defaultExchangeAndQueue() }
        verify { testRabbitService.consumeQueue() }
    }

    // 预创建Mock的RabbitService
    private val testRabbitService: RabbitService = mockk(relaxUnitFun = true)
    // 测试模块,提供Mock实例并配置行为
    private val testModule: Module = module {
        single { testRabbitService }
        every { testRabbitService.defaultExchangeAndQueue() } returns Unit
        every { testRabbitService.consumeQueue() } returns Unit
        every { testRabbitService.giveMeFactory() } returns mockk<ConnectionFactory>()
    }
}

原理说明

通过依赖注入,将RabbitService的实例控制权交给DI容器:

  • 生产环境:容器提供真实的RabbitService实例,正常连接RabbitMQ
  • 测试环境:容器提供Mockk创建的模拟实例,所有方法调用都被mock拦截,不会触发真实的连接逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:45:05