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

Python全局类实例打补丁测试断言失败问题排查

Python全局类实例打补丁测试断言失败问题排查

问题场景

我的项目结构如下:

├── core   
|    └── service
|         └── file.py
└── tests 
     └── test_file.py

在file.py里,我定义了一个依赖全局类实例的函数fun:

# file.py
from another_module import myClass
cls_instance=myClass()

def fun(): 
    return cls_instance.request()

我想通过patch来测试这个函数,于是写了test_file.py:

# test_file.py
from core.service.file import fun
class TestCase(unittest.TestCase): 
    @patch("core.service.file.cls_instance")
    def setUp(self, mock): 
        self.mock = mock.return_value

    def test_fun(self):
        self.mock.request.return_value = 1
        resp=fun()
        self.assertEqual(resp, 1)

但测试失败了,错误信息是:

Expected : None
Actual   : 1

这到底哪里出错了?

问题分析

你遇到的问题主要出在两个关键点上:

1. Mock对象的赋值逻辑错误

当你用@patch("core.service.file.cls_instance")装饰setUp时,传入的mock参数本身就是被补丁替换后的cls_instance对象,而不是一个用来生成实例的mock工厂。你写的self.mock = mock.return_value完全是画蛇添足——这个return_value是把mock当作可调用对象(比如类)时才会用到的属性,但这里我们补丁的是一个已经实例化好的对象,所以mock.return_value默认是None,直接导致你的self.mock变成了None。

2. setUp上的@patch作用域问题

unittest的@patch装饰器如果用在setUp方法上,补丁只会在setUp方法执行期间生效,当setUp执行完毕,补丁就会被自动撤销。这就导致你在test_fun方法中调用fun()时,实际上用的还是原来的cls_instance,而不是你在setUp里准备的mock对象。

解决方案

这里给你两种正确的写法,任选其一即可:

方案一:直接在测试方法上使用@patch

把补丁直接装饰在测试方法上,这样补丁会在整个测试方法执行期间生效,mock参数也会直接传入测试方法:

from core.service.file import fun
import unittest
from unittest.mock import patch

class TestCase(unittest.TestCase): 
    @patch("core.service.file.cls_instance")
    def test_fun(self, mock_cls_instance): 
        # 给补丁后的实例的request方法设置返回值
        mock_cls_instance.request.return_value = 1
        resp = fun()
        self.assertEqual(resp, 1)

方案二:在setUp/tearDown中手动管理补丁

如果你需要在多个测试方法中复用这个mock,可以用上下文管理器手动启动和停止补丁:

from core.service.file import fun
import unittest
from unittest.mock import patch

class TestCase(unittest.TestCase): 
    def setUp(self):
        # 启动补丁,获取mock对象
        self.patch_instance = patch("core.service.file.cls_instance")
        self.mock_cls_instance = self.patch_instance.start()

    def tearDown(self):
        # 测试结束后停止补丁,恢复原对象
        self.patch_instance.stop()

    def test_fun(self):
        self.mock_cls_instance.request.return_value = 1
        resp = fun()
        self.assertEqual(resp, 1)

这样修改后,你的测试就能正确断言fun()返回1了。

备注:内容来源于stack exchange,提问作者rhar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:58:04