Type hints don't get propagated to spy
adriangonz opened this issue · 4 comments
When using spy()
to wrap a method, it seems that the type hints from the original method don't get propagated to the spy itself.
As an example, if we think of the following method foo
:
def foo(x: int) -> str:
return "bar"
When calling typing.get_type_hints(foo)
, it will get us the whole set of type hints. However, when calling get_type_hints
on the spied method, nothing will be returned:
from typing import get_type_hints
get_type_hints(foo) # -> will return `{ "x": int, "return": str }`
spied = mocker.spy(mod, "foo")
get_type_hints(spied) # -> will return `{ }`
Hi @adriangonz,
spy()
returns a MagicMock
, because the use case is to actually verify that the original method was called correctly by the tested code, while at the same time still call the original method, not to call the returned value of spy
yourself in the tests.
So the annotation seems correct to me, or do you have a valid use case for actually calling the returned value from spy
?
Hi @nicoddemus ,
Thanks for that context.
In that case, I may have understood wrong, but isn't the spy meant to be used in place of the original function? As in, won't it have the same signature?
In my case, the use case is that I need to spy a function to validate that it gets called with the right args. However, the signature of the function in question is also important in the business logic (as it looks at the input args and return values of the method). However, the signature mismatch above means that I cannot use spy()
for that.
In that case, I may have understood wrong, but isn't the spy meant to be used in place of the original function? As in, won't it have the same signature?
No, it is meant just to provide an object that you can use to inspect if the original function was called with the right arguments, while also calling the original function.
# file: foo.py
def foo(x):
print("foo called")
return x
def bar(x):
return foo(x) * 2
# file: test_foo.py
from foo import bar
def test_foo(mocker):
foo_spy = mocker.spy("app.foo")
assert bar(5) == 10
assert foo_spy.call_args == mocker.call(5)
So we can check that foo
was called with the expected arguments, while the original foo
implementation is still called.
However, the signature mismatch above means that I cannot use spy() for that.
Yes I'm afraid so.
Closing for now, let me know if you have further questions.
Oooooh, I see! Ok, I just saw the light now. 🤯
In that case, we can just pass the original method as before, and just use the spy for the assertions. That's even more helpful. Thanks a lot!