How to mock all the methods of one class - Python

Hi Everyone,

I am trying to mock an entire class using patch.
Imagine that class isMyClass() with methods foo() and bar() both of which return a boolean.

The way I was attempting it was by including fixture in the conftest.py for example

@pytest.fixture(scope="session")
def mock_myclass() -> MagicMock:
    with patch("module.file.MyClass") as myclass_mocked:
         myclass_mocked.foo.return_value = True
         myclass_mocked.bar.return_value = True
         yield myclass_mocked

Then I have a test called test_some_other_function for example

def test_some_other_function(mock_myclass: MagicMock, x: int, y:float):
    some_other_function(x,y)

Where some_other_function is not a member of MyClass but uses an object from MyClass and calls both foo and bar methods. However when I try either mock_myclass.foo.asseert_called() or mock_myclass.bar.asseert_called() I always get the error below, even though I am positive the test is reaching the places where foo and bar are called.

 AssertionError: Expected 'foo' to have been called.

I hope this was clear. Thanks!

Are you sure you’re patching the right thing?

Remember that when a module imports objects from your module, then the patch needs to be done in that other module. For example:

# mod_a.py
from aiosmtpd.handlers import Maildir

In this case, I have to patch mod_a.Maildir instead of aiosmtpd.handlers.Maildir

Hi @pepoluan

I think I am importing correctly because when I try to patch each method individually like

@pytest.fixture(scope="function")
def mock_myclass_foo() -> MagicMock:
    with patch("module.file.MyClass.foo") as mock_foo:
        mock_foo.return_value = True
        yield mock_foo

@pytest.fixture(scope="function")
def mock_myclass_bar() -> MagicMock:
    with patch("module.file.MyClass.bar") as mock_bar:
        mock_bar.return_value = True
        yield mock_foo

and call the test as

def test_some_other_function(mock_foo: MagicMock, mock_bar: MagicMock, x: int, y:float):
    some_other_function(x,y)

The patched methods actually get called in some_other_function
The thing is that myclass has so many methods that I would like to just patch them all together in the same fixture

What you’re moving is the class (or equivalently in this case, the class constructor). In your actual module, you’ll likely be instantiating that class, then calling foo and bar on the instance. The return value of mock_myclass returns the instance, whose methods are different from what you’ve set.

You can either change your assertions to use foo etc on the return value of mock_myclass or patch all three methods of the actual class.

Option 1:

@pytest.fixture
def mock_myclass():
    with patch("module.file.MyClass") as myclass_mocked:
         myclass_mocked.return_value.foo.return_value = True
         myclass_mocked.return_value.bar.return_value = True
         yield myclass_mocked

def test_some_other_function(mock_myclass, x, y):
    some_other_function(x, y)
    mock_myclass.return_value.foo.assert_called_once()

Option 2:

@pytest.fixture
def mock_myclass():
    from module.file import MyClass
    with contextlib.ExitStack() as stack:
        stack.push(patch.object(MyClass, "foo", Mock(return_value=True)))
        yield

def test_some_other_function(mock_myclass, x, y):
    from module.file import MyClass
    some_other_function(x, y)
    MyClass.foo.assert_called_once()
    assert instance(MyClass.foo.call_args_list[0][0][0], MyClass)
1 Like

thanks @EpicWink !
That helped but wasn’t the entire problem.
for others that might come across this issue remember “patch where you use it, not where you define it” this means that
with patch("module.file.MyClass") as myclass_mocked:
needs to be
with patch("module.fileWhereMyClassIsUsed.MyClass") as myclass_mocked: