如何找出名称在运行时解析为哪个对象

问题描述 投票:0回答:1

要修补对象,需要使用调用站点的正确路径。 这足够繁琐,可以在 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
,但假设所有内容都在包中,规范形式应该可以工作。]

python unit-testing pytest introspection monkeypatching
1个回答
1
投票

如果问题是修补类内部的内容,您实际上可以在原始模块中的类中修补方法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()
)

© www.soinside.com 2019 - 2024. All rights reserved.