Glutexo
Glutexo

Reputation: 566

How to mock the cls argument of a classmethod?

I have a class that uses a classmethod as a custom constructor.

# my_module.py
class MyClass:
    def __init__(self, my_arg):
        self.my_arg = my_arg

    @classmethod
    def my_constructor(cls):
        return cls("my val")

I would like to assert in a test that the constructor has been called with a proper value and the instance returned.

I tried several ways of patching, but I couldn’t get it working.

I tried patching the whole class, using the wraps argument to have the real method executed.

# my_test.py
from unittest import TestCase
from unittest.mock import patch

from my_module import MyClass


class MyClassTestCase(TestCase):
    @patch("my_module.MyClass", wraps=MyClass)
    def test_my_constructor(self, my_class):
        result = my_class.my_constructor()
        my_class.assert_called_once_with("my val")
        self.assertEqual(result, my_class.return_value)

The problem here is that the cls argument that the _my_module.MyClass.my_class_ method gets is not my Mock, but the real MyClass class.

My other idea was to use patch.object to patch the real __init__ constructor, but that doesn’t work either. This time because __init__ should not return anything, but MagicMock has a return value.

# my_test.py
from unittest import TestCase
from unittest.mock import patch

from my_module import MyClass


class MyClassTestCase(TestCase):
    @patch.object(MyClass, "__init__")
    def test_my_constructor(self, init):
        result = MyClass.my_constructor()
        init.assert_called_once_with("my val")
        self.assertEqual(result, init.return_value)

Is there an actual way to assert the MyClass class has been instantiated by the _my_constructor_ with the given arguments and returned?

Upvotes: 3

Views: 474

Answers (1)

mock __new__ instead __init__ and assert will be init.assert_called_once_with(MyClass, "my val")

Upvotes: 0

Related Questions