94

I want to be able to have multiple calls to a particular attribute function return a different result for each successive call.

In the below example, I would like increment to return 5 on its first call and then 10 on its second call.

Ex:

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

The below page has almost everything that I need except that it assumes that the caller would be an object named "mock", but this can't be assumed.

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

1

4 Answers 4

140

You can just pass an iterable to side effect and have it iterate through the list of values for each call you make.

@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)
Sign up to request clarification or add additional context in comments.

2 Comments

Also, this seems to work: @mock.patch("A.increment", side_effect=[5, 10])
NB: This does not work directly for callables. For a function f, mock.side_effect = f, m() == f(); but if mock.side_effect = [f, f, f], then m() == f (the function object, rather than the function actually being called).
12

I tested and this should work

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'
        '...'
    ]

Version

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

Comments

7

I think the popping values off of a list method will be more straightforward. The below example works for the test you wanted to perform.

Also, I've had a difficult time with the mock library before and have found that the mock.patch.object() method was typically easier to use.

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)

Comments

1

You can use patch and set the absolute path to the module.

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

The order used in patches must be kept in method mock parameters, module1 refers to mock_get and module2 refers to mock_post.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.