Python使用Mock模拟多次调用并返回不同的结果

77

我希望能够对一个属性函数进行多次调用,每次调用都返回不同的结果。

在下面的例子中,我希望第一次调用increment时返回5,第二次调用返回10。

例如:

import mock

class A:
    def __init__(self):
        self.size = 0
    def increment(self, amount):
        self.size += amount
        return amount

@mock.patch("A.increment")
def test_method(self, mock_increment):
    def diff_inc(*args):
        def next_inc(*args):
            #I don't know what belongs in __some_obj__
            some_obj.side_effect = next_inc
            return 10
        return 5

    mock_increment.side_effect = diff_inc

下面的页面几乎拥有我所需的一切,但它假设调用者将是一个名为“mock”的对象,但这不能假设。

http://mock.readthedocs.org/en/latest/examples.html#multiple-calls-with-different-effects


3
可能是Python mock multiple return values的重复问题。 - Sardorbek Imomaliev
4个回答

125

你可以将一个可迭代对象传递给副作用函数,让它在每次调用时遍历该值列表。

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)

9
此外,这似乎是有效的:@mock.patch("A.increment", side_effect=[5, 10]) - santon
这不能直接用于可调用对象。对于一个函数f,mock.side_effect = fm() == f();但如果mock.side_effect = [f, f, f],那么 m() == f(是函数对象,而不是实际调用的函数)。 - gerrit

11

我测试过了,应该可以正常工作。

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

版本

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0

6

我认为从列表中弹出值的方法会更加直接。下面的例子适用于您想要执行的测试。

此外,我以前使用mock库遇到过困难,发现mock.patch.object()方法通常更容易使用。

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)

这正是我一直在寻找的! - lmiguelvargasf

0

您可以使用patch并设置模块的绝对路径。

from unittest.mock import patch

@patch("src.module2.requests.post")
@patch("src.module1.requests.get")
def test_mock(self, mock_get, mock_post):
   data = {}
   mock_post.return_value.status_code = 200
   mock_post.return_value.json.return_value = data
   mock_get.return_value.json.return_value = data

在补丁中使用的顺序必须保持在方法模拟参数中,module1 指的是 mock_get,module2 指的是 mock_post。


为什么被踩票? - WestCoastProjects

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接