Python Mock类方法被调用但未被检测到的问题排查
Python Mock中patch模拟类后,断言实例方法调用失败的问题解决
问题场景
在学习Python Mock时,使用patch模拟Facade类,被测函数main会创建该类实例并调用其install方法。测试中能断言类被成功调用,但install方法的调用断言失败,尽管调试打印显示方法确实被调用,期望assert facadeMock.install.called返回True但实际失败。
被测代码(install/init.py)
from .facade import Facade def main(): f = Facade() f.install() print('jf-debug-> "f.install.called": {value}'.format( value=f.install.called))
测试代码(test/install_tests.py)
import os import sys # 允许导入目标包 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from unittest.mock import patch import install @patch('install.Facade') # 使用autospec=True也未改变结果 def test_main_with_links_should_call_facade_install_with_link_true(facadeMock): install.main() assert facadeMock.called assert facadeMock.install is install.Facade.install assert facadeMock.install.called # <-------------------- 此处断言失败!
测试输出
============================= test session starts ============================== platform linux -- Python 3.10.6, pytest-7.2.0, pluggy-1.0.0 rootdir: /home/jfl/ubuntu-vim, configfile: pytest.ini collected 1 item test/install_tests.py jf-debug-> "f.install.called": True F =================================== FAILURES =================================== ________ test_main_with_links_should_call_facade_install_with_link_true ________ facadeMock = <MagicMock name='Facade' id='140679041900864'> @patch('install.Facade') def test_main_with_links_should_call_facade_install_with_link_true(facadeMock): install.main() assert facadeMock.called assert facadeMock.install is install.Facade.install > assert facadeMock.install.called E AssertionError: assert False E + where False = <MagicMock name='Facade.install' id='140679042325216'>.called E + where <MagicMock name='Facade.install' id='140679042325216'> = <MagicMock name='Facade' id='140679041900864'>.install test/install_tests.py:21: AssertionError =========================== short test summary info ============================ FAILED test/install_tests.py::test_main_with_links_should_call_facade_install_with_link_true - AssertionError: assert False ============================== 1 failed in 0.09s ===============================
问题原因
测试参数中的facadeMock是被patch替换后的类本身的Mock对象,而被测函数main()中调用Facade()创建的是该Mock类的实例对象。你断言的facadeMock.install是类级别的方法Mock,实际被调用的是实例对象上的install方法,两者并非同一个Mock对象,因此断言失败。
解决方案
通过facadeMock.return_value获取main()中实际创建的实例Mock,再断言该实例的install方法是否被调用:
修改测试代码中的断言部分:
actualInstance = facadeMock.return_value assert actualInstance.install.called
完整修改后的测试函数:
@patch('install.Facade') def test_main_with_links_should_call_facade_install_with_link_true(facadeMock): install.main() assert facadeMock.called actualInstance = facadeMock.return_value assert actualInstance.install.called # 此时断言会成功
补充说明
使用patch模拟类时的核心逻辑:
facadeMock是类的Mock,调用它(即Facade())会返回一个实例Mock,该实例Mock被存储在facadeMock.return_value中- 实例上的方法调用,必须通过
return_value访问对应的方法Mock才能完成正确断言
内容的提问来源于stack exchange,提问作者Jean-Francois
相关产品推荐
相关产品推荐

