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

mock异步方法时assert_called_once_with为何出现不同对象ID?

问题根因

你遇到的协程对象内存ID不一致问题,本质是AsyncMock的调用特性导致的:

  • 被mocker.patch替换的异步方法会生成AsyncMock实例,每次调用AsyncMock都会生成一个全新的协程对象,哪怕入参完全一致,不同次调用生成的协程内存地址也不相同。
  • 业务代码执行self.func_to_be_mocked(x, y)时,生成了第一个协程对象,作为参数传给了create_task,这是真实传入的入参。
  • 你在断言语句里写的mocked_func(1, 2),会触发AsyncMock的第二次调用,生成第二个全新的协程对象,拿这个新生成的对象去和之前的实际入参做匹配,必然会出现内存ID不一致、断言失败的问题。
  • 你的测试代码还存在两个额外问题:一是断言中使用的mocked_func_to_be_mocked变量并未定义,你实际声明的mock变量名为mocked_func;二是mocked_func.return_value = mocked_func属于无效写法,异步mock的return_value是协程执行完成后的返回结果,不需要赋值为mock对象本身,这行可以直接删除。
正确修复方案

你不需要手动调用mock方法生成协程来做参数匹配,正确的校验逻辑分两步即可:

  1. 校验被mock的异步方法func_to_be_mocked确实被正确的参数调用过
  2. 校验create_task确实被调用,且传入的参数是合法协程对象

参考实现代码:

import asyncio
from unittest.mock import ANY

class Tests():
    def test_func_to_be_tested(self, mocker):
        test_class = ClassUnderTest()
        mocked_func = mocker.patch("class_under_test.ClassUnderTest.func_to_be_mocked")
        mocked_event_loop = mocker.patch("asyncio.AbstractEventLoop")
        mocked_task = mocker.patch("asyncio.Task")
    
        mocker.patch("asyncio.new_event_loop", return_value=mocked_event_loop)
        mocker.patch("asyncio.AbstractEventLoop.create_task", return_value=mocked_task)
            
        test_class.func_to_be_tested(1, 2)
  
        # 校验异步方法被正确参数调用
        mocked_func.assert_called_once_with(1, 2)
        # 校验create_task调用逻辑,写法1:显式校验传入参数是协程
        mocked_event_loop.create_task.assert_called_once()
        call_args, _ = mocked_event_loop.create_task.call_args
        assert asyncio.iscoroutine(call_args[0])

        # 校验create_task调用逻辑,写法2:更简洁的ANY匹配
        # mocked_event_loop.create_task.assert_called_once_with(ANY)

内容的提问来源于stack exchange,提问作者Lexie-fox

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 10:51:27