gpt4 book ai didi

python - 让 pytest 中的 monkeypatching 工作

转载 作者:行者123 更新时间:2023-12-01 22:12:46 26 4
gpt4 key购买 nike

我正在尝试使用 pytest 开发一个测试,用于从字符串列表中随机选择一个字符串的类方法。

它看起来基本上像下面的 givemeanumber 方法:

import os.path
from random import choice

class Bob(object):
def getssh():
return os.path.join(os.path.expanduser("~admin"), '.ssh')

def givemeanumber():
nos = [1, 2, 3, 4]
chosen = choice(nos)
return chosen

类 Bob 中的第一个方法 getssh 只是 pytest docs 中的示例

我的生产代码从数据库中获取字符串列表,然后随机选择一个。所以我希望我的测试获取字符串,然后选择第一个字符串而不是随机选择。这样我就可以针对已知字符串进行测试。

根据我的阅读,我认为我需要使用 monkeypatching 来伪造随机化。

这是我到目前为止所得到的

import os.path
from random import choice
from _pytest.monkeypatch import MonkeyPatch
from bob import Bob

class Testbob(object):
monkeypatch = MonkeyPatch()

def test_getssh(self):
def mockreturn(path):
return '/abc'
Testbob.monkeypatch.setattr(os.path, 'expanduser', mockreturn)
x = Bob.getssh()
assert x == '/abc/.ssh'

def test_givemeanumber(self):
Testbob.monkeypatch.setattr('random.choice', lambda x: x[0])
z = Bob.givemeanumber()
assert z == 1

第一个测试方法同样是 pytest 文档中的示例(因为我在测试类中使用它而略有改动)。这很好用。

按照我希望使用的文档中的示例Testbob.monkeypatch.setattr(random, 'choice', lambda x: x[0])但这会产生NameError:名称“随机”未定义

如果我把它改成Testbob.monkeypatch.setattr('random.choice', lambda x: x[0])

它更进一步但没有发生换出:断言错误:断言 2 == 1

monkeypatching 是完成这项工作的正确工具吗?如果是我哪里出错了?

最佳答案

问题出在 Python 中如何处理变量名称。与其他语言的主要区别在于,没有通过名称将值分配给变量;只有变量名与对象的绑定(bind)。

这是一个更大的话题,超出了这个问题的范围,但结果如下:

  1. 当您从模块 random 导入一个函数 choice 时,您将一个名称 choice 绑定(bind)到存在于该处的函数在导入的那一刻,并将此名称放在 bob 模块的本地命名空间中。

  2. 当您修补 random.choice 时,您将模块 random 的名称 choice 重新绑定(bind)到新的模拟对象.

  3. 但是,bob 模块中已经导入的名称仍然指的是原始函数。因为没有人修补它。函数本身没有修改,只是名称被替换了。

  4. 因此,Bob 类调用原始的 random.choice 函数,而不是模拟函数。

要解决此问题,您可以采用以下两种方式之一(但不能同时采用两种方式,因为它们相互冲突):


A:总是通过那个确切的全名调用 random.choice() 函数(即不是 choice)。当然,import random 之前(不是 from random import ...)——与您对 os.path.expanduser().

# bob.py
import os.path
import random

class Bob(object):
@classmethod
def getssh(cls):
return os.path.join(os.path.expanduser("~admin"), '.ssh')

@classmethod
def givemeanumber(cls):
nos = [1, 2, 3, 4]
chosen = random.choice(nos) # <== !!! NOTE HERE !!!!
return chosen

B:修补您调用的实际函数,在这种情况下是 bob.choice()(不是 random.choice())。

# test.py
import os.path
from _pytest.monkeypatch import MonkeyPatch
from bob import Bob

class Testbob(object):
monkeypatch = MonkeyPatch()

def test_givemeanumber(self):
Testbob.monkeypatch.setattr('bob.choice', lambda x: x[0])
z = Bob.givemeanumber()
assert z == 1

关于名称未知的原始错误 random:如果您想要 patch(random, 'choice', ...),那么您必须 import random — 即将名称 random 绑定(bind)到正在修补的模块。

当您只执行 from random import choice 时,您将名称 choice 绑定(bind)到变量的本地命名空间,而不是 random

关于python - 让 pytest 中的 monkeypatching 工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46890843/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com