简体   繁体   中英

Pass parameter to side_effect function for patching in unittest.mock

I'm using patch from unittest.mock to change the behavior of a remote API call in my test.

I have three different functions that return three different json files that represent the mock data to be returned from the API. For each mock api call, I am setting side_effect to be one of these functions. This pattern isn't DRY, but I don't know how to pass a parameter to a side_effect function.

The three mock api call functions look like this:

def mock_api_call_1():
    with open('path/to/mock_1.json') as f:
        mock_data = json.load(f)
    return mock_data

Here's my test

class MyTest(TestCase):

    def test_api(self):

        with patch('mymodule.utils.api_call', side_effect=mock_api_call_1):
            do_crud_operations()
            self.assertEqual(MyModel.objects.all().count(), 10)

        with patch('mymodule.utils.api_call', side_effect=mock_api_call_2):
            do_crud_operations()
            self.assertEqual(MyModel.objects.all().count(), 11)

How can I refactor this code to be able to pass a parameter to the side_effect ( mock_call(1) instead of mock_call_1 ).

From the unittest docs , I see that:

side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT, the return value of this function is used as the return value.

I see that the function passed to side_effect takes the same arguments as the mock, but I'm still not sure how best to use mock to accomplish this. I'll eventually want to add more test cases, so I don't want to be hard-coding different mock_api_call functions.

Use a lambda function:

from unittest import TestCase, main
from unittest.mock import Mock, patch
import sys

def mock_api_call(x):
    print(x)

class MyTest(TestCase):
    def test_api(self):
        with patch('sys.exit',
                    side_effect=lambda x: mock_api_call(x)) as m:
            m(0)
            sys.exit(0)

            m(1)
            sys.exit(1)


if __name__ == '__main__':
    main()

I think the easiest way to do this is to set side_effect to a function that returns a function.

def mock_call(num):
    def wrapper():
        with open("path/to/mock_{num}.json") as f:
            data = json.load(f)
        return data
    return wrapper

Now I can pass mock_call(1) to side_effect and it will behave as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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