如何使用MockK模拟构造函数传入的回调?以ResponseReceiver为例
问题:MockK测试callbackFlow中的回调参数捕获失败
我有一个包含回调逻辑的Receiver类,在callbackFlow内部创建了Server回调实例并传入ResponseReceiver。现在想用MockK测试这个类,但无法捕获或模拟ResponseReceiver(any())接收的回调参数。
Receiver类代码
class Receiver(private val context: Context) { val responses: Flow<Response> = callbackFlow { val callback = object : Server { override fun broadcast(rsp: Response) { trySend(rsp) } } val receiver: BroadcastReceiver = ResponseReceiver(callback) register(receiver) awaitClose { unregister(receiver) } } private fun register(receiver: BroadcastReceiver) = context.registerReceiver(receiver, IntentFilter(CMD)) private fun unregister(receiver: BroadcastReceiver) = context.unregisterReceiver(receiver) }
测试代码
@Test fun given_when_then() = runTest { // Prepare every { ResponseReceiver(any()) } answers { firstArg<Server>().broadcast(mockk()) } // Act val testObserver = target.responses.test() verify { mockContext.registerReceiver(any(), any()) } testObserver .assertValue(response) .finish() }
解决方案
你的测试思路存在问题:直接mockResponseReceiver构造函数并触发回调的方式,既没模拟真实广播触发的场景,也没正确捕获到Receiver内部创建的Server实例。下面是两种可行的解决方式:
方式一:Mock构造函数并捕获回调
用mockkConstructor来mockResponseReceiver的构造逻辑,同时捕获传入的Server实例,后续手动模拟广播触发来调用回调:
@Test fun given_when_then() = runTest { val mockResponse = mockk<Response>() var capturedServer: Server? = null // 开启ResponseReceiver的构造mock mockkConstructor(ResponseReceiver::class) { // 构造时保存传入的Server实例 every { ResponseReceiver(any()) } answers { capturedServer = firstArg() mockk() // 返回一个mock的BroadcastReceiver实例 } // 模拟广播被接收时的逻辑,调用捕获到的Server回调 every { anyConstructed<ResponseReceiver>().onReceive(any(), any()) } answers { capturedServer?.broadcast(mockResponse) } // 订阅Flow val testObserver = target.responses.test() // 验证接收器已注册 verify { mockContext.registerReceiver(any(), any()) } // 手动触发广播接收(模拟系统发送广播) anyConstructed<ResponseReceiver>().onReceive(mockContext, mockk()) // 断言Flow收到预期数据 testObserver .assertValue(mockResponse) .finish() } }
方式二:重构代码优化可测试性
如果可以修改Receiver类,建议把ResponseReceiver的创建逻辑抽离成依赖,比如通过构造函数传入工厂方法,这样就不用mock构造函数:
// 修改后的Receiver类 class Receiver( private val context: Context, private val receiverFactory: (Server) -> BroadcastReceiver = ::ResponseReceiver ) { val responses: Flow<Response> = callbackFlow { val callback = object : Server { override fun broadcast(rsp: Response) { trySend(rsp) } } val receiver = receiverFactory(callback) register(receiver) awaitClose { unregister(receiver) } } // ... 原有注册/注销逻辑不变 }
对应的测试代码会更简洁:
@Test fun given_when_then() = runTest { val mockResponse = mockk<Response>() var capturedServer: Server? = null // 创建自定义的接收器工厂,捕获Server实例 val mockReceiverFactory = mockk<(Server) -> BroadcastReceiver>() every { mockReceiverFactory.invoke(any()) } answers { capturedServer = firstArg() mockk { every { onReceive(any(), any()) } answers { capturedServer?.broadcast(mockResponse) } } } // 初始化测试目标时传入自定义工厂 val target = Receiver(mockContext, mockReceiverFactory) // 订阅Flow val testObserver = target.responses.test() verify { mockContext.registerReceiver(any(), any()) } // 触发广播接收 (mockReceiverFactory.invoke(mockk()) as BroadcastReceiver).onReceive(mockContext, mockk()) testObserver .assertValue(mockResponse) .finish() }
内容的提问来源于stack exchange,提问作者Pause
相关产品推荐
相关产品推荐

