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

Python unittest中Mock其他模块导入的类属性的方法

问题描述

项目结构

├───pa
│   |───__init__.py
|   |─── a1.py
|   |─── test_a1.py
|
├───pb
│   |───__init__.py
|   |─── b1.py

对应代码

b1.py

class B1:

    def __init__(self):
        self.text = "Unmocked_B1"

    def get_text(self):
        return self.text

pb/init.py

from .b1 import B1

a1.py

from src.pb import B1


class A1:

    def __init__(self):
        self.b = B1()

    def get_text(self):
        return self.b.get_text()

pa/init.py

from .a1 import A1

需求

使用unittest.mock.patch mock A1类中的self.b属性,让test_a1.py执行后打印出Mocked B1。当前测试代码框架如下:

from unittest import TestCase
from unittest.mock import patch, PropertyMock
from . import A1


class TestB1(TestCase):

    @patch('Some args')
    def test_get_text(self, mocked_b1):
        # Some code
        
        a1 = A1()
        print(a1.get_text())  # Should print: Mocked B1

无效尝试

  1. Mock B1原始类
@patch('src.pb.b1.B1')
def test_get_text(self, mocked_b1):
    mocked_b1.get_text.return_value = 'Mocked B1'

    a1 = A1()
    print(a1.get_text())  # 未得到预期结果
  1. 用PropertyMock mock类属性
@patch('pa.a1.A1.b', new_callable=PropertyMock)
def test_get_text(self, mocked_b):
    mocked_b.get_text.return_value = 'Mocked B1'

    a1 = A1()
    print(a1.get_text())  # 未得到预期结果

需要找到支持动态属性场景的有效mock方式,使用Python 3.10。


解决方案

方法一:Mock被测试模块中的B1引用

问题出在mock路径错误——必须mock被测试代码实际导入的B1引用(即pa.a1模块中的B1),而非原始类的路径。修改后的测试代码:

from unittest import TestCase
from unittest.mock import patch
from . import A1


class TestB1(TestCase):

    @patch('pa.a1.B1')
    def test_get_text(self, mocked_b1):
        # 给mock类的实例配置返回值
        mocked_b_instance = mocked_b1.return_value
        mocked_b_instance.get_text.return_value = 'Mocked B1'

        a1 = A1()
        print(a1.get_text())  # 输出: Mocked B1

解释

  • @patch('pa.a1.B1') 替换的是A1模块内导入的B1类,当A1初始化调用B1()时,会生成这个mock类的实例。
  • 必须给mock类的实例(mocked_b1.return_value)设置get_text的返回值,而非直接给mock类设置,因为self.b是实例对象。

方法二:直接替换A1实例的b属性

如果不想mock整个B1类,可以直接替换已创建A1实例的self.b属性:

from unittest import TestCase
from unittest.mock import Mock, patch
from . import A1


class TestB1(TestCase):

    def test_get_text(self):
        a1 = A1()
        # 创建mock的B1实例
        mocked_b = Mock()
        mocked_b.get_text.return_value = 'Mocked B1'
        
        # 临时替换a1的b属性
        with patch.object(a1, 'b', mocked_b):
            print(a1.get_text())  # 输出: Mocked B1

也可以用装饰器跳过A1的初始化,手动设置mock属性:

from unittest import TestCase
from unittest.mock import Mock, patch
from . import A1


class TestB1(TestCase):

    @patch.object(A1, '__init__', return_value=None)
    def test_get_text(self, mock_init):
        a1 = A1()
        mocked_b = Mock()
        mocked_b.get_text.return_value = 'Mocked B1'
        a1.b = mocked_b
        
        print(a1.get_text())  # 输出: Mocked B1

无效原因说明

  1. 第一种尝试:mock的是原始模块的src.pb.b1.B1,但A1模块已经导入了B1的引用,mock原始模块不会影响已导入的引用,必须mock被测试模块内的引用。
  2. 第二种尝试:A1.b是实例属性(在__init__中赋值的self.b),而PropertyMock仅用于mock类的@property装饰属性,对实例属性无效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 20:10:26