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

Python按名称导入模块方法时patch mock失效问题咨询

mock time.sleep() 失效问题解决方案

问题复现

尝试mock time.sleep()时出现三类异常表现:

  • 写法1:mock不生效,sleep()仍执行真实延时
import unittest
from unittest.mock import patch

from time import sleep

class TestTime(unittest.TestCase):

    @patch('time.sleep', return_value=None)
    def test_time(self, mock_time):
        sleep(10) # 真实等待10秒

if __name__ == '__main__':
    unittest.main()
  • 写法2:mock正常生效,无真实延时
import unittest
from unittest.mock import patch

import time

class TestTime(unittest.TestCase):

    @patch('time.sleep', return_value=None)
    def test_time(self, mock_time):
        time.sleep(10) # 瞬间执行完成

if __name__ == '__main__':
    unittest.main()
  • 写法3:直接抛出TypeError: Need a valid target to patch. You supplied: 'sleep'错误
import unittest
from unittest.mock import patch

from time import sleep

class TestTime(unittest.TestCase):

    @patch('sleep', return_value=None) # 触发类型错误
    def test_time(self, mock_time):
        sleep(10)

if __name__ == '__main__':
    unittest.main()

运行环境:

  • Python版本:3.8.12
  • 操作系统:Windows 10 21H1 (19043.1766)

核心诉求:无需将所有模块内的sleep()调用修改为time.sleep()形式,实现正确mock。

失效根因

表现差异由Python导入机制和unittest.mock.patch的工作逻辑共同决定:

  1. 执行from time import sleep时,会将time模块下的sleep函数引用直接拷贝到当前模块的命名空间,当前模块的sleep与time.sleep是两个独立名称,初始指向同一个函数对象。
  2. @patch('time.sleep')仅会替换time模块命名空间下的sleep绑定,不会修改其他模块已经完成导入的sleep引用,因此写法1调用当前模块下的sleep时,仍然会执行真实的延时逻辑。
  3. patch要求传入的替换目标必须符合包路径.模块名.属性名的格式,直接传入sleep缺少模块上下文,无法定位待替换对象,因此写法3直接抛出参数错误。

可行方案

不需要修改所有调用为time.sleep(),两种方案可直接生效:

方案1:patch对应模块命名空间下的sleep引用

哪个模块里直接用了from time import sleep并调用sleep(),就patch哪个模块路径下的sleep属性即可。
比如测试文件本身名为test_sleep.py,直接patch当前模块下的sleep:

import unittest
from unittest.mock import patch

from time import sleep

class TestTime(unittest.TestCase):
    # 替换当前模块命名空间下的sleep引用,而非time模块下的
    @patch('test_sleep.sleep', return_value=None)
    def test_time(self, mock_time):
        sleep(10) # 瞬间执行,无真实延时
        mock_time.assert_called_once_with(10)

if __name__ == '__main__':
    unittest.main()

如果是业务模块(例如路径为src/utils.py)中写了from time import sleep并调用,测试时直接patchsrc.utils.sleep即可,不需要修改业务代码的调用形式。

方案2:提前全局patch time.sleep

如果需要跨所有模块统一mock time.sleep,不想逐个模块配置patch路径,可以在测试入口最顶部、所有业务/测试模块导入time.sleep之前,提前启动patch替换time.sleep,后续所有模块不管是import time调用time.sleep,还是from time import sleep导入到自身命名空间,拿到的都是mock对象:

# 测试入口文件最开头执行patch逻辑
import unittest
from unittest.mock import patch
import time
# 提前替换time模块下的sleep
sleep_patcher = patch('time.sleep', return_value=None)
sleep_patcher.start()

# 所有导入逻辑放在patch启动之后
from time import sleep

class TestTime(unittest.TestCase):
    def test_time(self):
        sleep(10) # 无真实延时
        time.sleep(5) # 同样无真实延时

if __name__ == '__main__':
    unittest.main()
    # 测试执行完成后可按需恢复真实sleep
    sleep_patcher.stop()

注意:该方案必须保证patch启动逻辑在所有from time import sleep导入语句之前执行,否则提前完成导入的模块仍然会持有真实sleep的引用,导致mock失效。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:51:31