Python MagicMock how to mock __contains__ method

302 Views Asked by At

I have defined the following class:

class Config:

     def __init__(self) -> None:
        self._entries = dict()
 
    @property
    def entries(self):
        return self._entries
    
    def __contains__(self, key: any) -> bool:

        """
        Returns True if specified key is in the existing config
        entries, else returns False.
        """

        return key in self.entries

I then have another class that is calling Config.__contains__ through:

def some_function():
    config_obj = Config()
    if entry in config_obj:
        # Do something.

Now, I want to unit-test some_function and mock Config.__contains__. To do so, I have defined this test function:

from unittest.mock import MagicMock

def test_some_function()
    
    # given
    mocked_config_obj = Config()
    mocked_config_obj.__contains__ = MagicMock(return_value=True)
       
    # when
    some_function()

    # then
    mocked_config_obj.__contains__.assert_called_once() # Fails

Ideally, if entry in config_obj should call mocked_config_obj.__contains__, but it's not. What am I missing here?

1

There are 1 best solutions below

0
larsks On

You're never actually mocking anything. You've created a MagicMock object and assigned it to a variable named mocked_config_obj, but you never patch the module containing some_function to use that mocked object.

Assuming that we have module config.py that implements the Config class:

class Config:

    def __init__(self) -> None:
        self._entries = dict()

    @property
    def entries(self):
        return self._entries

    def __contains__(self, key: any) -> bool:

        """
        Returns True if specified key is in the existing config
        entries, else returns False.
        """

        return key in self.entries

And module some_module.py that implements SomeClass, which uses config.Config:

from config import Config


class SomeClass:
    def some_function(self, entry):
        config_obj = Config()
        if entry in config_obj:
            print("doing something")

Then we could write a test like this:

from unittest import mock

import some_module

def test_some_function():
    with mock.patch('some_module.Config') as mock_config_obj:
        mock_config_obj.return_value.__contains__.return_value = True
        obj = some_module.SomeClass()
        obj.some_function("entry")

        mock_config_obj.return_value.__contains__.assert_called_with('entry')

That arranges for config_obj = Config() to receive a mock object, which we can then test for calls once some_function returns.