Search code examples
pythonunit-testingpython-3.xmockingpython-mock

Mock: Tracking all Calls. Shell vs. program inconsistency


Why does the content of mock.mock_calls differ depending on whether I run in shell or in a program? The assertion of mock.mock_calls == expected fails in a program but passes in the shell. The code is based on the documentation for tracking multiple calls. (26.5.1.5. Tracking all Calls)

>>> from unittest.mock import MagicMock, call
>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='57725952'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='57775944'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
>>> mock.method_calls
[call.method(), call.attribute.method(10, x=53)]
>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> expected == mock.mock_calls
True
>>> expected == mock.method_calls
True

So far that is all exactly as expected. But look at what happens to mock.mock_calls in a program: It records additional calls to the __str__ method of every method called. mock.method_calls is unaffected.

import unittest.mock
mock = unittest.mock.MagicMock()
print(mock.method())
print(mock.attribute.method(10, x=53))
print('\nmock.mock_calls\n', mock.mock_calls)
print('\nmock.method_calls\n', mock.method_calls)

expected = [unittest.mock.call.method(),
            unittest.mock.call.attribute.method(10, x=53)]
print(expected == mock.mock_calls)
print(expected == mock.method_calls)

Output:

<MagicMock name='mock.method()' id='45740272'>
<MagicMock name='mock.attribute.method()' id='45850128'>

mock.mock_calls
 [call.method(),
 call.method().__str__(),
 call.attribute.method(10, x=53),
 call.attribute.method().__str__()]

mock.method_calls
 [call.method(), call.attribute.method(10, x=53)]
False
True

I am running Python 3.4 on a Windows 7 computer.


Solution

  • This is because you are printing the method calls. It causes the __str__() magic method to be called:

    print(mock.method())
    print(mock.attribute.method(10, x=53))
    

    Eliminate the print() call and you'll see the desired behavior:

    mock.method()
    mock.attribute.method(10, x=53)
    

    FYI, example from the console:

    >>> mock = unittest.mock.MagicMock()
    >>> print(mock.method())
    <MagicMock name='mock.method()' id='4433183576'>
    >>> print(mock.attribute.method(10, x=53))
    <MagicMock name='mock.attribute.method()' id='4433223752'>
    >>> mock.mock_calls
    [call.method(),
     call.method().__str__(),
     call.attribute.method(10, x=53),
     call.attribute.method().__str__()]
    

    Or, alternatively, use a normal Mock class:

    >>> mock = unittest.mock.Mock()
    >>> print(mock.method())
    <Mock name='mock.method()' id='4433235528'>
    >>> print(mock.attribute.method(10, x=53))
    <Mock name='mock.attribute.method()' id='4433252648'>
    >>> mock.method_calls
    [call.method(), call.attribute.method(10, x=53)]
    >>> mock.mock_calls
    [call.method(), call.attribute.method(10, x=53)]