pdb.set_trace()在monkeypatch替换内置open的测试中执行失败的解决方案咨询
这个问题我之前也碰到过,核心原因就是你用lambda替换了内置open,但这个lambda只接收一个位置参数,而pdb初始化时会尝试读取~/.pdbrc配置文件,调用open时会传入encoding关键字参数,你的lambda不支持该参数,所以抛出TypeError。下面给你几个不用修改被测函数接口的可行解决办法:
方法1:编写兼容的mock open函数(推荐)
不要用简单的lambda,而是写一个包装函数,把非测试场景的open调用转发给原始内置函数,只对测试相关的请求返回mock的StringIO。这样pdb调用open读配置文件时,会走原始逻辑,不会触发错误。
代码示例:
import builtins import io import pytest def count_unique_letters_in_file(path: str): with open(path) as f: return len(set(c.lower() for c in f.read() if c.isalpha())) def test_unique(monkeypatch: pytest.MonkeyPatch): text = 'Quick frog or something' # 先保存原始的内置open函数 original_open = builtins.open def mock_open(path, *args, **kwargs): # 处理测试函数的open请求:返回mock的StringIO # 如果你测试用的路径不固定,可以去掉路径判断,直接返回mock # 但保留路径判断能避免影响其他合法的open调用 if path == 'blah.txt': return io.StringIO(text) else: # 转发所有非测试的open请求到原始函数,支持所有参数 return original_open(path, *args, **kwargs) monkeypatch.setattr(builtins, 'open', mock_open) # 现在pdb调用open时会走原始函数,不会报错了 import pdb; pdb.set_trace() assert count_unique_letters_in_file('blah.txt') == 15
这个方法的优点是通用性强,既不破坏测试逻辑,也能让pdb正常工作,甚至支持你在调试过程中使用pdb的所有功能。
方法2:禁用pdb的配置文件读取
pdb初始化时默认会读取~/.pdbrc,如果我们禁用这个行为,它就不会调用open去读配置文件,自然也就不会触发你的mock lambda。
方式A:代码中指定参数
在调用pdb时手动关闭readrc选项:
import pdb # 禁用读取pdbrc,避免触发mock的open pdb.Pdb(readrc=False).set_trace()
方式B:命令行参数
用pytest的--no-pdbrc参数启动调试,所有pdb会话都不会读取配置文件:
pytest --pdb --no-pdbrc
这个方法的优点是操作简单,不需要修改mock逻辑,但缺点是如果你平时依赖~/.pdbrc里的自定义配置(比如快捷键、颜色主题),调试时就无法使用这些配置了。
方法3:临时恢复原始open(仅适合特定调试场景)
如果你只是想在mock后调试mock的状态,不需要调试测试函数的执行过程,可以用monkeypatch.context()临时恢复原始open:
def test_unique(monkeypatch: pytest.MonkeyPatch): text = 'Quick frog or something' original_open = builtins.open monkeypatch.setattr(builtins, 'open', lambda _: io.StringIO(text)) # 进入pdb前暂时恢复原open with monkeypatch.context() as m: m.setattr(builtins, 'open', original_open) import pdb; pdb.set_trace() # 退出上下文后,mock会自动恢复 assert count_unique_letters_in_file('blah.txt') == 15
注意:这个方法的局限性很大——当你在pdb中继续执行到测试函数的open调用时,mock已经恢复,会导致测试函数尝试读取真实的blah.txt文件(大概率不存在)而报错,所以只适合调试mock的设置状态,不适合调试测试函数的执行流程。
总结
最推荐使用方法1,因为它从根源上解决了mock函数的兼容性问题,兼顾测试逻辑和调试需求。如果只是临时快速调试,方法2的命令行参数是最高效的选择。
内容来源于stack exchange

