繁体   English   中英

我可以模拟在 Python 测试中的另一个 function 调用中调用的 function 返回吗?

[英]Can I mock a function return that is called within another function call in a Python Test?

有没有办法模拟在另一个 function 调用中调用的 function 返回? 例如:

def bar():
    return "baz"

def foo():
    return bar()

class Tests(unittest.TestCase):
    def test_code(self):
        # hijack bar() here to return "bat" instead
        assert(foo() == "bat")

我试过使用@mock.patch ,但发现它只允许我模拟我正在调用的 function,而不是 function,因为调用不同的 function 而被调用。

通用补丁

unittest.mock.patch完全按照我的其他答案建议开箱即用。 你可以根据需要添加任意多的@patch注解,你select的对象将被打上补丁:

from unittest.mock import patch

def bar():
    return "baz"

def foo():
    return bar()

class Tests(unittest.TestCase):
    @patch(__name__ + '.bar', lambda: 'bat')
    def test_code(self):
        assert(foo() == "bat")

在此配置中,function bar将在test_code完成后恢复。 如果您希望将相同的补丁应用到您的 class 中的所有测试用例,请注释整个 class:

@patch(__name__ + '.bar', lambda: 'bat')
class Tests(unittest.TestCase):
    def test_code(self):
        assert(foo() == "bat")

补丁全局

您还可以在全局命名空间上使用unittest.mock.patch.dict来获得相同的结果:

class Tests(unittest.TestCase):
    @patch.dict(globals(), {'bar': lambda: 'bat'})
    def test_code(self):
        assert(foo() == "bat")

您可以编写一个上下文管理器来临时换出全局命名空间中的对象:

class Hijack:
    def __init__(self, name, replacement, namespace):
        self.name = name
        self.replacement = replacement
        self.namespace = namespace

    def __enter__(self):
        self.original = self.namespace[self.name]
        self.namespace[self.name] = self.replacement

    def __exit__(self, *args):
        self.namespace[self.name] = self.original

您可以使用被劫持的方法调用您的模拟 function:

def bar():
    return "baz"

def bar_mock():
    return "bat"

def foo():
    return bar()

class Tests(unittest.TestCase):
    def test_code(self):
        with Hijack('bar', bar_mock, globals()):
            assert(foo() == "bat")

这是一种非常通用的方法,可以在单元测试之外使用。 事实上,将其概括为适用于任何可以表示为某种映射的可变 object 非常简单:

class Hijack:
    def __init__(self, name, replacement, namespace, getter=None, setter=None):
        self.name = name
        self.replacement = replacement
        self.namespace = namespace
        self.getter = type(namespace).__getitem__ if getter is None else getter
        self.setter = type(namespace).__setitem__ if setter is None else setter

    def __enter__(self):
        self.original = self.getter(self.namespace, self.name)
        self.setter(self.namespace, self.name, self.replacement)

    def __exit__(self, *args):
        self.setter(self.namespace, self.name, self.original)

对于类和其他对象,您可以使用getter=getattrsetter=setattr 对于None优于KeyError的情况,您可以使用getter=dict.get等。

此外,您可以使用monkeypatch monkeypatch fixture 的seattr方法。 对于第一个 arguments,它接受一个 object 进行修补,或者一个字符串将被解释为带点的导入路径,最后一部分是属性名称:

# foo_module.py
def bar():
    return "baz"


def foo():
    return bar()

# test_foo.py
from foo_module import foo


def test_foo(monkeypatch):
    monkeypatch.setattr('foo_module.bar', lambda: 'bat')
    assert foo() == "bat"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM