3

To start, I'm using the pytest-mock and pytest packages to test a software project that I'm working on.

I have a situation where I'm testing a class in a module and trying to mock out a composite object that it instantiates from its __init__ method. When I do this, I am able to assert that the __init__ method is properly called, but when I try to assert that an instance method is called, I get an assertion error.

When I debug pytest by passing the --pdb option to it, I verify by checking the MagicMock() object's .method.called property that it "wasn't" called, but then when I check the MagicMock() object's .method_calls property, I see that it was in fact called.

Here's a simple example to show what I mean:

test.py

import module

def test_Calling(mocker):
    mocker.patch('module.Called', autospec=True)

    module.Calling('argument')

    module.Called.assert_called_once_with('argument')
    module.Called.check_me.assert_called_once()

module.py

class Calling(object):
    def __init__(self, argument):
        called_instance = Called(argument)
        called_instance.check_me()

class Called(object):
    def __init__(self, argument):
        pass

    def check_me(self):
        pass

And here's the pytest/pdb session:

(venv) C:\pytest-issue>pytest --pdb test.py
============================= test session starts =============================
platform win32 -- Python 2.7.12, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
rootdir: C:\pytest-issue, inifile:
plugins: mock-1.5.0
collected 1 items

test.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

mocker = <pytest_mock.MockFixture object at 0x000000000431B2E8>

    def test_Calling(mocker):
        mocker.patch('module.Called', autospec=True)

        module.Calling('argument')

        module.Called.assert_called_once_with('argument')
>       module.Called.check_me.assert_called_once()

test.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

_mock_self = <MagicMock name='Called.check_me' spec='instancemethod' id='70367608'>

    def assert_called_once(_mock_self):
        """assert that the mock was called only once.
        """
        self = _mock_self
        if not self.call_count == 1:
            msg = ("Expected '%s' to have been called once. Called %s times." %
                   (self._mock_name or 'mock', self.call_count))
>           raise AssertionError(msg)
E           AssertionError: Expected 'check_me' to have been called once. Called 0 times.

venv\lib\site-packages\mock\mock.py:915: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> c:\pytest-issue\venv\lib\site-packages\mock\mock.py(915)assert_called_once()
-> raise AssertionError(msg)
(Pdb) up
> c:\pytest-issue\test.py(9)test_Calling()
-> module.Called.check_me.assert_called_once()
(Pdb) module.Called.method_calls
[call().check_me()]
(Pdb) module.Called.check_me.called
False

For the life of me, I'm not sure what I'm doing wrong. In this circumstance, do I need to assert .Called.method_calls? If so, that looks like I would have to put a lot of logic into my tests, which is not desirable.

1 Answer 1

5

So, it turned out that I was quite ignorant in how you test a patched module.

import module

def test_Calling(mocker):
    mock_Called = mocker.patch('module.Called')

    module.Calling('argument')

    mock_called.assert_has_calls([
        mocker.call('argument'),
        mocker.call().check_me()])

The important things to consider here are that you need to store the return value of mocker.patch, as that will give a reference to the MagicMock object that gets instantiated and patched in over module.Called. Next, when you wish to test that an instance method is being called, you should use the assert_has_calls method on the MagicMock instance, passing it call() arguments that reflect what is being called. call() by itself refers to the __new__/__init__ calls, and call().method refers to a call made against an instance method.

Since figuring this out, I've been able to use the pytest-mock module quite successfully.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.