kaste / mockito-python

Mockito is a spying framework
MIT License
123 stars 12 forks source link

Invocation of mocked @classmethod defined in super classes fails. #33

Closed nielsvaneck closed 4 years ago

nielsvaneck commented 4 years ago

I recently tried mocking a class method and saw my test failing with a cryptic message Fail: TypeError: missing a required argument: 'id' and an exception somewhere in the python library code.

I grabbed the mockito-python code, did some digging and found that the only thing that set my case apart from what mockito-python supports (and has tests for πŸ‘) is that in my case, the class method I tried mocking, was defined on a super class of the object in when().

Here's a little example that replicates the issue:

class LoudBarker:
    @classmethod
    def loud_bark(cls, bark):
        return bark

class Dog(LoudBarker):
    @classmethod
    def bark(cls):
        return "woof!"

class LoudBarkerTest(TestBase):
    def test_loud_dog(self):
        when(Dog).loud_bark("woof!").thenReturn("WOOOOF!")
        self.assertEqual(Dog.loud_bark("woof!"), "WOOOOF!") #fails with TypeError

Here, Dog.loud_bark("woof!") will fail with Fail: TypeError: missing a required argument: 'bark'.

If we write the mocking statement based on the class where the class method is defined, everything works as expected:

    def test_loud_bark__in_loud_barker(self):
        when(LoudBarker).loud_bark("woof!").thenReturn("WOOOOF!")
        self.assertEqual(Dog.loud_bark("woof!"), "WOOOOF!") #passes

In practice, this way of defining when() may be difficult because libraries do not always exactly expose in what class a class method is defined and source code may not always be available to find the answer.