Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a call to __next__

I am trying to mock a coroutine. As such, this mock's __next__() and close() are called. While mocking close() works, I cannot mock __next__():

    mock_coroutine = mock.Mock()
    mock_coroutine.some_call(1, 2, 3)
    mock_coroutine.some_call.assert_called_with(1, 2, 3)
    mock_coroutine.close()
    mock_coroutine.close.assert_called_with()
    #this fails
    mock_coroutine.__next__()
    mock_coroutine.__next__.assert_called_with()

What am I missing? How to make sure my mock's __next__() method is called?

For now, I am using the following:

class MockCoroutine:
    def __next__(self):
        self.called_next = True

    def close(self):
        self.called_exit = True

However, I very much rather use a standard mock.

like image 624
AlexC Avatar asked Aug 17 '15 13:08

AlexC


1 Answers

You need to use MagicMock, not Mock, to have magic methods like __next__ available by default:

>>> from unittest import mock
>>> mock_coroutine = mock.MagicMock()
>>> mock_coroutine.__next__()
<MagicMock name='mock.__next__()' id='4464126552'>
>>> mock_coroutine.__next__.assert_called_with()

Quoting from the documentation:

Mock allows you to assign functions (or other Mock instances) to magic methods and they will be called appropriately. The MagicMock class is just a Mock variant that has all of the magic methods pre-created for you (well, all the useful ones anyway).

So, alternatively, you could still use the regular Mock object, but then you need to explicitly add that attribute:

>>> mock_coroutine = mock.Mock()
>>> mock_coroutine.__next__ = mock.Mock()
>>> mock_coroutine.__next__()
<Mock name='mock.__next__()' id='4464139232'>

That's because although Mock creates attributes on the fly as you access them, any attributes with leading and trailing underscores are explicitly exempted from that. See this footnote:

The only exceptions are magic methods and attributes (those that have leading and trailing double underscores). Mock doesn’t create these but instead raises an AttributeError. This is because the interpreter will often implicitly request these methods, and gets very confused to get a new Mock object when it expects a magic method. If you need magic method support see magic methods.

But note that the magic methods are usually meant to be looked up on the class, not the instance, so directly adding the __next__ attribute to a Mock instance can still fail; the MagicMock class takes care of this specific issue for you.

like image 98
Martijn Pieters Avatar answered Sep 24 '22 17:09

Martijn Pieters