Search code examples
pythonunit-testingmockingpython-unittestpython-mock

Python Mock - How to get the return of a MagicMock as if it was a normal method


For example:

import mock

class MyClass(object):
    def foo(self, x, y, z):
        return (x, y, z)


class TestMyClass(TestCase)
    @mock.patch('MyClass')
    def TestMyClass(self, MyClassMock):
        foo_mock = MyClassMock.foo()

        self.assertEquals((x, y, z), foo_mock)

So, the real question is: How to get the return of that test intead of getting this <MagicMock name='MyClass.foo()' id='191728464'> or how to deal with this MagicMock object to get the return of that test which should be a tuple containing 3 elements and nothing more or less?

Any suggestion, any idea, any argument will be welcome. Thanks in advance!


Solution

  • If you trying to test if MyClass.foo() works correctly, you should not mock it.

    Mocking is used for anything outside the code-under-test; if foo called another, external function some_module.bar(), then you'd mock some_module.bar() and give it a staged return value:

    import some_module
    
    class MyClass(object):
        def foo(self, x, y, z):
            result = some_module.bar(x, y, z)
            return result[0] + 2, result[1] * 2, result[2] - 2
    
    class TestMyClass(TestCase):
        @mock.patch('some_module.bar')
        def test_myclass(self, mocked_bar):
            mocked_bar.return_value = (10, 20, 30)
    
            mc = MyClass()
    
            # calling MyClass.foo returns a result based on bar()
            self.assertEquals(mc.foo('spam', 'ham', 'eggs'),
                (12, 40, 28))
            # some_class.bar() was called with the original arguments
            mocked_bar.assert_called_with('spam', 'ham', 'eggs')
    

    Here I set mocked_bar.return_value to what should be returned when the mocked some_module.bar() function is called. When the code-under-test actually calls bar(), the mock returns that value.

    When you don't set a return_value a new MagicMock() object is returned instead, one that'll support further calls, and you can test for those calls just like on the mocked_bar object, etc.