Reputation: 1103
I am having a hard time mocking an instance of an object.
I would like to write a unit test to test 'my_func' function that uses an instance of a class. I know how to mock class functions, however, I do not know how to mock an instance of the class (object) itself (not a function).
Inside my module file:
# my_module.py
import fancypackage1
import fancypackage2
def my_func():
x = fancypackage1.SomeClass.somefunction() # I know how to mock this
myclient = fancypackage2.Client() # I don't know how to mock this
myresult = do_something(myclient, x) # I know how to mock this
return myresult
Inside my test file:
# test_my_module.py
import pytest
import mock
import fancypackage1
import fancypackage2
from my_module import my_func
def test_my_func(mocker):
mock_someclass_somefunction = mocker.patch('my_module.fancypackage1.SomeClass.somefunction')
mock_someclass_somefunction.return_value = 'hello'
mock_client = mocker.patch.object(fancypackage2.Client, '__init__') # TypeError: __init__() should return None, not 'MagicMock'
mock_do_something = mocker.patch('my_module.do_something')
my_func()
mock_do_something.assert_called_with(mock_client, 'hello')
Since I did not know how to mock an instance of a class, but I knew how to mock a class method, I figured that perhaps, for the instance of the class, using the constructor function might work - and so I used init, but this did not work for me unfortunately, I am getting an error:
E TypeError: __init__() should return None, not 'MagicMock'
After the above did not work, I tried passing a custom-made fixture:
@pytest.fixture
def client_constructor_mock():
my_client = fancypackage2.Client()
return my_client
def test_my_func(mocker, client_constructor_mock):
mock_someclass_somefunction = mocker.patch('my_module.fancypackage1.SomeClass.somefunction')
mock_someclass_somefunction.return_value = 'hello'
mock_client = client_constructor_mock
mock_do_something = mocker.patch('my_module.do_something')
my_func()
mock_do_something.assert_called_with(mock_client, 'hello')
Unfortunately, this did not work either. The error I am getting:
> mock_do_something.assert_called_with(mock_client, 'hello')
E AssertionError: Expected call: do_something(<fancypackage2.Client object at 0x000001E6896A69C8>, 'hello')
E Actual call: do_something(<fancypackage2.Client object at 0x000001E689721488>, 'hello')
which tells me that there are two different objects of class Client, and that's the error.
I am at a loss here, how do I ensure that myclient is mocked correctly? Any help is very much appreciated.
Upvotes: 8
Views: 8155
Reputation: 121
__init__
cannot be patched directly for manipulating the instances created by the class, as the TypeError
suggests.
This can be done by patching the class and requesting the return_value
of that mock
-object, which is the result of calling the __init__
of that class.
Instead of
mock_client = mocker.patch.object(fancypackage2.Client, '__init__') # TypeError: __init__() should return None, not 'MagicMock'
The following should work:
mock_client_class = mocker.patch('my_module.fancypackage2.Client')
mock_client = mock_client_class.return_value
Upvotes: 6