5

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.

Nodame
  • 1,053
  • 3
  • 12
  • 18

1 Answers1

5

__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
Tim Muller
  • 111
  • 7