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

如何Mock Python模块根层级的@property属性?

如何Mock模块根层级的@property属性?

问题背景

我需要Mock模块根层级定义的@property属性,相关代码如下:
some_module.py

@property
def conn(self):
    if not get_request():
        raise SomeError(
            "Some error happened"
        )
    return get_request().connection


Database.conn = conn

我尝试了多种Mock方式均未成功,包括:

@mock.patch("some_module.conn")
def test_some_test(mock_conn):
    mock_conn.return_value = mock.Mock()
@mock.patch("some_module.conn", new_callable=PropertyMock)
def test_some_test(mock_conn):
    mock_conn.return_value = mock.Mock()
@mock.patch("some_module.conn", new_callable=PropertyMock, return_value=mock.Mock())
def test_some_test():
    ...

我知道如果@property属于类(如下示例),上述部分方法是有效的,但我的场景必须使用模块根层级的属性,目前无法实现Mock:

class MyClass:
    @property
    def conn(self):
        if not get_request():
            raise SomeError(
                "Some error happened"
            )
        return get_request().connection

更新尝试:将conn视为全局变量Mock也失败:

@mock.patch("some_module.conn", mock.Mock())
def test_some_test():
    ...

问题原因

模块级@property和类内@property的工作机制有差异:类内的property是类的描述符,而模块级property是直接绑定到模块对象的属性,访问时会触发其__get__方法。另外如果测试/被测试代码中使用from some_module import conn的导入方式,直接patch原模块的conn不会影响已导入的局部变量引用,这是常见的失效原因。

解决方案

1. 针对模块引用的正确Mock方式

如果被测试代码通过import some_module并访问some_module.conn,可以用PropertyMock直接patch模块属性:

from unittest.mock import patch, PropertyMock, Mock
import some_module

@patch("some_module.conn", new_callable=PropertyMock)
def test_module_property(mock_conn):
    # 设置property的返回值
    mock_conn.return_value = Mock()
    # 验证访问模块级conn是否返回预设的mock对象
    assert some_module.conn is mock_conn.return_value

2. 针对直接导入的情况

如果被测试代码使用from some_module import conn导入属性,需要patch被测试代码中的conn引用(而非原模块的):
假设被测试文件tested_code.py:

from some_module import conn

def get_connection():
    return conn

测试代码:

from unittest.mock import patch, PropertyMock, Mock
import tested_code

@patch("tested_code.conn", new_callable=PropertyMock)
def test_imported_property(mock_conn):
    mock_conn.return_value = Mock()
    assert tested_code.get_connection() is mock_conn.return_value

3. 直接替换模块属性的兜底方案

如果上述方法仍不生效,可以直接操作模块的__dict__替换属性,绕过描述符机制:

from unittest.mock import Mock
import some_module

def test_direct_replace():
    mock_conn = Mock()
    # 直接替换模块的conn属性
    some_module.__dict__["conn"] = mock_conn
    assert some_module.conn is mock_conn
    # 测试完成后可恢复原属性(可选)
    del some_module.__dict__["conn"]

4. 注意:若实际访问的是Database类的属性

你的代码中把conn赋值给了Database.conn,如果被测试代码实际访问的是Database类的conn属性,需要patch类的属性而非模块属性:

from unittest.mock import patch, PropertyMock, Mock
import some_module

@patch("some_module.Database.conn", new_callable=PropertyMock)
def test_class_property(mock_conn):
    mock_conn.return_value = Mock()
    db = some_module.Database()
    assert db.conn is mock_conn.return_value

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 10:33:12