I have written a custom class to mock a general API client in a codebase so that I can centrally and easily mock all of the class methods for unit testing. This is working great so far, however I am looking for a way to track individual calls to each class method. Right now that only trackable call via Mock is the initial class instantiation.
Here is the mock class:
from faker import Factory
faker = Factory.create()
class MockAPIClass
def get_some_data(self, data):
return f"{data} - {faker.pyint()}"
Then in my util file:
def func_to_test_that_calls_client(arg):
client = regular_api_client()
return client.get_some_data(arg)
Then in my unit tests:
from unittest import mock
from django.test import TransactionTestCase
from .file import MockAPIClass
from .util import func_to_test_that_calls_client
class TestUils(TransactionTestCase):
def setUp(self):
self.api_call_patcher = mock.patch('path.to.mocked.class.instantiation')
self.patch_api = self.api_call_patcher.start()
self.mock_api = MockAPIClass() # this done so that the mocked class can be referenced below
self.patch_api.return_value = self.mock_api
def tearDown(self):
mock.patch.stopall()
def test_util_func(self):
res = func_to_test_that_calls_client("test")
self.assertTrue(res)
self.patch_api.assert_called_once()
The above functions exactly as expected and intended. However, inside of the funciton func_to_test_that_calls_client
, the original client is instantiated then the class method get_some_data()
is called. With this implementation, I have no visibility into the call stack of the class methods like that function, only the parent instantiation of the class. I would like to be able to see for example that func_to_test_that_calls_client
was called with "test"
with this current implementation. Is there a way to do this with mock or some other python trick?
A standard Mock
object (which is what patch
uses by default) works fine. There's no need to build your own special mock class.
Using this as a simulated version of your util.py
(I needed to add a dummy regular_api_client
to have a target for patch
):
regular_api_client = lambda: None
def func_to_test_that_calls_client(arg):
client = regular_api_client()
return client.get_some_data(arg)
this simple test seems to do what you're after:
from unittest.mock import patch
from util import func_to_test_that_calls_client
def test_util_func():
with patch('util.regular_api_client') as mock_client_class:
mock_client = mock_client_class()
func_to_test_that_calls_client("test")
mock_client.get_some_data.assert_called_with("test")
Note that you can validate that the above test works (I always second-guess myself with mock methods on whether I actually called a "real" mock method or another mock, heh) by changing assert_called_with("test")
to assert_called_with("something else")
and seeing that the test now fails:
E AssertionError: expected call not found.
E Expected: get_some_data('something else')
E Actual: get_some_data('test')