2

I must be tired, because surely there is an easy way to do this. But I've read over the pytest docs and can't figure out this simple use case.

I have a little package I want to test:

class MyClass:
    def __init__(self):
        pass
    def my_method(self, arg):
        pass

def the_main_method():
    m = MyClass()
    m.my_method(123)

I would like to ensure that (1) an instance of MyClass is created, and that (2) my_method is called, with the proper arguments.

So here's my test:

from unittest.mock import patch

@patch('mypkg.MyClass', autospec=True)
def test_all(mocked_class):

    # Call the real production code, with the class mocked.
    import mypkg
    mypkg.the_main_method()

    # Ensure an instance of MyClass was created.
    mocked_class.assert_called_once_with()

    # But how do I ensure that "my_method" was called?
    # I want something like mocked_class.get_returned_values() ...

I understand that each time the production code calls MyClass() the unittest framework whips up a new mocked instance.

But how do I get my hands on those instances?

I want to write something like:

the_instance.assert_called_once_with(123)

But where do I get the_instance from?

jwd
  • 10,837
  • 3
  • 43
  • 67

1 Answers1

1

Well, to my surprise, there is only one mock instance created, no matter how many times you call the constructor (:

What I can write is:

mocked_class.return_value.my_method.assert_called_once_with(123)

The return_value does not represent one return value, though — it accumulates information for all created instances.

It's a rather abstruse approach, in my mind. I assume it was copied from some crazy Java mocking library (:

If you want to capture individual returned objects, you can use .side_effect to return whatever you want, and record it in your own list, etc.

jwd
  • 10,837
  • 3
  • 43
  • 67
  • "Well, to my surprise, there are no mock instances created" - yeah there are. Why do you think there aren't? "The return_value does not represent one return value, though — it accumulates information for all created instances." - that's because by default, the mock only ever creates one return value and reuses it every time it's called. `return_value` is that return value. – user2357112 Feb 16 '17 at 19:18
  • Shouldn't you be testing `mocked_class.return_value.my_method.assert_called_once_with(123)`? – user2357112 Feb 16 '17 at 19:20
  • @user2357112: Regarding your first comment: as you say, by default, only one `return_value` instance is created, not one for each invocation of the constructor. That's what I was trying to get at with my answer. I'll reword it to be clearer. Right on the second point, I missed the method call piece. – jwd Feb 16 '17 at 23:58
  • @user2357112: You say "by default ..." - is there a way to change this default, so each return value is a distinct instance? – jwd Feb 16 '17 at 23:59
  • I don't think there's any better way to do that than setting `side_effect`. – user2357112 Feb 17 '17 at 00:04