如何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
相关产品推荐
相关产品推荐

