Search code examples
pytestpytest-mock

Pytest: Spy of magic method __call__


For use in Pytest, I would like to create a spy of the magic method __call__ of a class.

Here is a minimal example. Code base:

import pytest
from pytest_mock import MockFixture, MockType


class A:

    def __call__(self, x) -> None:
        print("__call__", x)


class B:

    def __init__(self, a: A) -> None:
        self.a = a

    def apply_a(self) -> None:
        self.a(42)

Test code:

@pytest.fixture
def a() -> A:
    return A()


@pytest.fixture
def b(a: A) -> B:
    return B(a)


@pytest.fixture
def a_application_spy(mocker: MockFixture, a: A) -> MockType:
    return mocker.spy(a, "__call__")


def test_apply_a(b: B, a_application_spy: MockType) -> None:
    b.apply_a()
    assert a_application_spy.called

The test test_apply_a fails.

The reason seems to be that the method __call__ is a magic method, which is used implicitly. The test succeeds if the implementation of B.apply_a is changed to

    def apply_a(self) -> None:
        self.a.__call__()

Is there a possibility to create the spy without having to change the code base?


Solution

  • This is a simple case of spying the wrong thing.

    Instead of:

    return mocker.spy(a, "__call__")
    

    You want:

    return mocker.spy(A, "__call__")