mockito-python icon indicating copy to clipboard operation
mockito-python copied to clipboard

Getting the mocked obj instance in the mock method

Open avandierast opened this issue 2 years ago • 5 comments

Hello ! First, I really like mockito-python :heart:

Now my problem :) If I want to mock the method of a class with thenAnswer and then have access to the instance self inside the stub function. I've tried different ways but I don't find any way to do so:

from mockito import when
class A:
    def __init__(self, a):
        self.a = a
   
    def method(self):
        print(self.a)

def fake_method(self_A):
    print("fake:", self_A.a)

when(A).method().thenAnswer(fake_method)
A(10).method()
# > TypeError: fake_method() missing 1 required positional argument: 'self_A'

With pytest.monkeypatch or pytest-mock , I usualy use functools.partialmethod to do that, but mockito tries to call directly the partialmethod and fails.

I've looked inside and the culprit seems to be the throw-away of self/cls in new_mocked_method so I think there is no way to get self in the mock.

Thank for the help. I'm open to do a PR but I failed to make it works easily....

avandierast avatar Jun 09 '22 15:06 avandierast

That is by design. ~~Initially thenAnswer was used internally and then exposed later~~*. Typically it makes writing answer-functions easier.

What's the use case here? Ideally answers from stubs/mocks are constants or sort of hard-coded.

* It is actually introduced by #1 and tries to mimic the java library

kaste avatar Jun 09 '22 19:06 kaste

I try to make a method fail in some cases, and only once. The solution i tired to implement was to thenAnswer the original method after a first mock that fails. I've seen the other ticket about thenCallRealMethod that would be really usefull. Currently, I've implemented my partial failing mock with classic mocking.

avandierast avatar Jun 10 '22 07:06 avandierast

That sounds like the other way around from your original posting because you want to call through to the original code. (In the OP you defined a function fake_method on the module level.)

There seems to be a bug or limitation here in that

> spy2(A.method)
> A().method()

also throws for the very same reason.

It works when you patch the instance though. E.g.

> a = A()
> spy2(a.foo)
> a.foo()

Not sure what to make with this. Do you need to patch A t.i. you don't have control over the instantiation? Example code would help probably.

kaste avatar Jun 10 '22 10:06 kaste

Ok, I will mix up my answers to be specific :)

I need to mock a function but only once. For instance, if there was thenCallRealMethod, I would do:

from mockito import when

class A:
    def method(self):
        print(self)

when(A).method().thenRaise(Exception).thenCallRealMethod()
with retry():
    A().method()

That's not true unit-tests with mocks. It is for integration tests but in non-nominal cases to check the robustsness of the code. The expressiveness of mockito would be really usefull even in those cases :)

avandierast avatar Jun 30 '22 13:06 avandierast

An implementation of thenCallOriginalImplementation is welcome. Of course, the example, if you test a retry-er you really want mocked/stubbed answers. A retry-er is generic.

For the code you gave, as I said, it at least works with instances.

a = A()
detached_method = a.method
when(a).method().thenRaise(ValueError).thenAnswer(detached_method).thenReturn("mocked")

This would be more convenient if we also had the reverse API. E.g.

doAnswer(a.method).when(a).method()

as we only need the detached_method trick because a.method gets patched before thenAnswer is evaluated.

That being said, I'm interested in the thenCallOriginalImplementation thing.

kaste avatar Jun 30 '22 19:06 kaste

Ah, we can close this as you fixed it.

kaste avatar Aug 30 '22 23:08 kaste