修补来自不同模块的多个方法(使用Python模拟)

Shy*_*der 7 python unit-testing

我模块的结构:

foo: 
  - load()  # from DB


bar:
  - check() # with user
  - take_action() 
Run Code Online (Sandbox Code Playgroud)

我想通过模拟加载和检查来测试take_action(它在执行操作之前基本上加载值并检查用户).

这是嘲笑:

mock_load  = Mock(side_effects=[<>, <>, <>]) # different data sets
mock_check = Mock(return_value=True)  # User approval
Run Code Online (Sandbox Code Playgroud)

我如何使用patch.multiple来实现这一目标?

with patch.multiple(??):
    # proceed to test
    take_action
Run Code Online (Sandbox Code Playgroud)

Mic*_*ico 16

简短的回答是你不能patch.multiple()用来做.如patch.multiple中所述,所有参数都将应用于所有创建的模拟,并且所有参数必须是同一对象的属性.您必须通过单个补丁调用来执行此操作.

不幸的是,你正在使用Python 2.6,所以你可以只使用nested氟里昂contextlib像指出蟒蛇:创建几个方面管理者"与"块多重背景下`with`在Python 2.6的语句.

也许更清洁,更简单的方法是@patch用作装饰器:

@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
def test_mytest(mock_check,mock_load):
    take_action()
    assert mock_load.called
    assert mock_check.called
Run Code Online (Sandbox Code Playgroud)

如果您在测试类的所有测试中都需要它,您可以装饰该类并在所有测试方法中使用模拟:

@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
class TestMyTest(unittest.TestCase)
    def test_mytestA(self,mock_check,mock_load):
        take_action()
        self.assertTrue(mock_load.called)
        self.assertTrue(mock_check.called)

    def test_mytestA(self,mock_check,mock_load):
        mock_check.return_value = False
        take_action()
        self.assertTrue(mock_load.called)
        self.assertTrue(mock_check.called)
Run Code Online (Sandbox Code Playgroud)

最后,你可以用它做的withcontextlib与第一例子变成:

from contextlib import nested

with nested(patch("foo.load",side_effects=["a","b","c"]), patch("bar.check",return_value=True)) as (mock_load, mock_check):
    take_action()
    assert mock_load.called
    assert mock_check.called
Run Code Online (Sandbox Code Playgroud)

......或者手工筑巢......

with patch("foo.load",side_effects=["a","b","c"]) as mock_load:
    with patch("bar.check",return_value=True)) as mock_check:
        take_action()
        assert mock_load.called
        assert mock_check.called
Run Code Online (Sandbox Code Playgroud)

我觉得装饰器是最易读和易用的.


Mar*_*arc 9

从 Python 3.10 开始,您可以使用带括号的上下文管理器来实现此目的,如下所示:

from unittest.mock import patch


def test_something():
    with (
        patch("foo.load", side_effect=["a","b","c"]),  # don't have to have `as`
        patch("bar.check", return_value=True) as mock_check,  # example using `as`
    ):
        # proceed to test
Run Code Online (Sandbox Code Playgroud)

您也可以手动嵌套。如果仍然使用End-Of-Lifed Python 2.6 或 2.7,您可以使用嵌套,如python 3 文档python 2 文档中所示:

with patch("foo.load", side_effect=["a","b","c"]) as mock_load:
    with patch("bar.check",return_value=True)) as mock_check:
        # proceed to test

# or you can use comma separated like version that has this structure
#
# with A() as a, B() as b:
#    ...
#
# which, if we applied it, would easily become a long one liner like so

with patch("bar.check",return_value=True)) as mock_check, patch("foo.load", side_effect=["a","b","c"]) as mock_load:
    # proceed to test

Run Code Online (Sandbox Code Playgroud)

在这些环境中,我通常选择通过这样做来保持清晰度:

def cron():
    with patch(
        "foo.load", side_effect=["a","b","c"]
    ) as mock_load, patch(
        "bar.check", return_value=True
    ) as mock_check, patch(
        "spam.Publish", return_value=42
    ) as mock_publish:
        # proceed to test
Run Code Online (Sandbox Code Playgroud)