Search code examples
pythonpython-3.xpytestmagicmockpytest-mock

Patch attribute of mocked class


I'm attempting to mock a class.

The class I'm attempting to mock looks like the following (lines have been removed for brevity):

class Connection(object):
    """Connection.
    """

    def __init__(self, base_url=None, creds=None, user_agent=None):
        self.clients = ClientFactory(self)

As you can see, it has a property called clients.

My method under test:

    def _auth(self, credentials):
        connection = Connection(base_url=f'https://someurl.com', creds=credentials)

        return connection.clients

My unit test looks like this:

@patch('connection.Connection.__init__')
def test_deploy(patched_connection, fs):
    patched_connection.return_value = None
    patched_connection.clients = None

    # Do some stuff

The question is... how do I set the clients property in my test as the method under test requires it to be set? (I can set it to None, but I just need to be able to set it.)

With the current code, my application returns the error:

AttributeError: 'Connection' object has no attribute 'clients'

Thanks!


Solution

  • You probably want to patch the Connection class itself, not the __init__ method:

    @patch('connection.Connection')
    def test_deploy(patched_connection, fs):
        connection_object = MagicMock()
        patched_connection.return_value = connection_object
        connection_object.clients = None
        sut = Auth()  # create the tested object (Auth is a placeholder here)
        sut._auth('')  # call the tested function
    
        # test for how `Connection` was constructed 
        patched_connection.assert_called_once_with(
            base_url='https://someurl.com', creds='')
    

    You patch the Connection class, and by setting return_value you set the Connection instance mock. Now you can set the wanted attributes in that instance.

    Note that checking for the __init__ call actually means to check for the instance creation call, so you can use the Connection mock for that.

    This is presuming that you don't want to test Connection itself, of course, and that _auth belongs to another class (here called Auth).