Nodame
Nodame

Reputation: 1103

How to mock a class instance (not a class function) in Python

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')

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

Answers (1)

Tim Muller
Tim Muller

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

Related Questions