Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot use attach_mock with an autospec function mock

Library module:

# mod.py
def foo():
    bar1("arg1")
    bar2("arg2x", "arg2y")

def bar1(x):
    pass

def bar2(x, y):
    pass

Test module:

# test_mod.py
from mod import foo

def test_foo(mocker):
    mock = mocker.MagicMock()
    mock.attach_mock(mocker.patch("mod.bar1"), "b1")
    mock.attach_mock(mocker.patch("mod.bar2", autospec=True), "b2")
    foo()
    mock.assert_has_calls(
        [
            mocker.call.b1("arg1"),
            mocker.call.b2("arg2x", "arg2y"),
        ]
    )

The mocker fixture is from pytest-mock plugin. Execute the MCVE with python -m pytest.

This test fails for weird reasons.

E       AssertionError: Calls not found.
E       Expected: [call.b1('arg1'), call.b2('arg2x', 'arg2y')]
E       Actual: [call.b1('arg1')]

Without the autospec it works. Does using autospec break the attach_mock feature? How should the test for foo assert on the order and args of the calls to dependencies bar1 and bar2 without losing their autospec?

like image 489
wim Avatar asked Dec 08 '25 09:12

wim


1 Answers

This was actually a bug in Python. It got fixed in late 2019, patched versions:

  • Python 3.8.2+
  • Python 3.7.5+

The test in original post is now passing on a fixed version. No backport for Python 3.6, which is security only now so will remain bugged forever. 🐛

These are the related PRs and issue tracker links:

bpo-21478: Autospec functions should propagate mock calls to parent

bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock

bpo-38473: Handle autospecced functions and methods used with attach_mock

Patch by Karthikeyan Singaravelan.

like image 173
wim Avatar answered Dec 09 '25 23:12

wim