laplasz
laplasz

Reputation: 3497

python mock - patching a method with itself - missing 1 required positional argument

I would like to patch a method to able to test whether it was called or not, but in the same time I dont want to loose the functionality so the idea was to patch the method with itself:

import unittest
from unittest.mock import Mock, patch


class MyClass():
   
    def foo(self, num):
        return num + 2


class myTestClass(unittest.TestCase):

    @patch.object(MyClass,'foo', Mock(wraps=MyClass.foo))
    def test_foo(self):
        my_class = MyClass()
        result = my_class.foo(2)
        my_class.foo.assert_called_once_with(2)
        self.assertEqual(result, 4)

During execution I am getting the following error:

File "/usr/lib64/python3.6/unittest/mock.py", line 1014, in _mock_call
return self._mock_wraps(*args, **kwargs)
TypeError: foo() missing 1 required positional argument: 'num'

Is this kind of patching possible in this way? There is probably a workaround described here

Upvotes: 2

Views: 4342

Answers (1)

QB_
QB_

Reputation: 109

I see several problems here:

  1. You are patching the class, not the object; but testing the object, not the class.
  2. You are wrapping with a Mock which I feel that it is wrong.
  3. And a minor one: you have named the object that you want to test as my_class, which can be misread easily.

I think that a right solution for your algorithm can be:


import unittest
from unittest.mock import Mock, patch


class MyClass():
   
    def foo(self, num):
        return num + 2


class myTestClass(unittest.TestCase):

    @patch.object(MyClass,'foo', wraps=MyClass.foo)
    def test_foo(self, mocked):
        obj = MyClass()
        result = MyClass.foo(obj, 2)
        MyClass.foo.assert_called_once_with(obj, 2)
        self.assertEqual(result, 4)


if __name__ == '__main__':
    unittest.main()

Note that:

  • wraps=MyClass.foo instead of wraps=Mock(...).
  • def test_do(self, mocked) instead of def test_do(self) (mock object is passed to the method).
  • MyClass.foo(obj, 2) since you have been patched the class, not the object.
  • MyClass.foo.assert_called_once_with(obj, 2) since you must check the class method (which you have been patched).

Alternatively, you can patch the object like in this test:


    def test_foo_object(self):
        obj = MyClass()
        with patch.object(obj, 'foo', wraps=obj.foo):
            result = obj.foo(2)
            obj.foo.assert_called_once_with(2)
        self.assertEqual(result, 4)

Upvotes: 1

Related Questions