要修补对象,需要使用调用站点的正确路径。 这足够繁琐,可以在 unittest 文档中获得自己的部分。
有时,当这很难弄清楚时,我宁愿在调用要修补的对象之前设置一个断点,然后检查它以查看实际查找的内容。 然后我可以退出调试器并修补正确的对象。 这感觉是一个简单的问题,类似于 shell 上的
which
,但我不知道该怎么做。
压缩文档中的示例:
a.py
class SomeClass: pass
b.py
from a import SomeClass
@breakpoint # resolve SomeClass into b.someclass here
def fn():
s = SomeClass()
现在我们想要测试 some_function 但我们想使用 patch() 模拟出 SomeClass [...] 如果我们使用 patch() 来模拟 a.SomeClass 那么它将对我们的测试没有影响[...]。
关键是在使用 SomeClass 的地方(或者查找它的地方)修补它。在这种情况下,some_function 实际上会在模块 b 中查找 SomeClass,我们已将其导入到该模块中。修补程序应如下所示:
@patch('b.SomeClass')
如何从调试器获取
b.SomeClass
?
[请注意,在给出的示例中导入很容易。 但有时事情足够深奥或复杂,可能需要一些令人费解的事情才能弄清楚。 一个单独的问题是从测试函数正确表达
b
,但假设所有内容都在包中,规范形式应该可以工作。]
如果问题是修补类内部的内容,您实际上可以在原始模块中的类中修补方法inside,并且它会起作用。
因此,假设在您的示例中您想要修补
Subclass.__init__
- 您只需 patch("a.Subclass.__init__")
即可。
现在,如果你想模拟整个子类,一种完全按照你要求做的方法是模拟它的
__init__
方法,就像我写的那样,但在其中插入 breakpoint()
- 这样你就可以手动检查并了解b.Subclass
。
这是一个 shell 会话,我在其中发出 shell 和 PDB 命令来查找它:
(env311) [gwidion@fedora tmp92]$ cat >a.py
class SomeClass: pass
(env311) [gwidion@fedora tmp92]$ cat >b.py
from a import SomeClass
def fn():
s = SomeClass()
(env311) [gwidion@fedora tmp92]$ cat >c.py
from unittest.mock import patch
import b
with patch("a.SomeClass.__init__", lambda self: (breakpoint(), ...)):
b.fn()
pass
(env311) [gwidion@fedora tmp92]$ python c.py
--Return--
> /home/gwidion/tmp92/c.py(6)<lambda>()->(None, Ellipsis)
-> with patch("a.SomeClass.__init__", lambda self: (breakpoint(), ...)):
(Pdb) l
1 from unittest.mock import patch
2
3 import b
4
5
6 -> with patch("a.SomeClass.__init__", lambda self: (breakpoint(), ...)):
7 b.fn()
8 pass
9
[EOF]
(Pdb) down
*** Newest frame
(Pdb) up
> /home/gwidion/tmp92/b.py(4)fn()
-> s = SomeClass()
(Pdb) l
1 from a import SomeClass
2
3 def fn():
4 -> s = SomeClass()
5
[EOF]
(Pdb) __name__
'b'
(Pdb)
(在断点调用正确之后,在与
breakpoint()
相同的作用域中使用某些内容非常重要,否则调试器将不允许我们与该作用域进行交互,并会直接跳转到调用的作用域 - 这就是为什么我让 lambda 返回 (breakpoint(), ...)
,而不仅仅是 breakpoint()
)