How to autospec mock functions with a decorator that injects a parameter?

Viewed 52

This is a toy version of a setup I have:

import mock
import functools

def quux(f):
    @functools.wraps(f)
    def wrapper(self, *args, **kwargs):
        return f(self, 'spam', *args, **kwargs)
    return wrapper

class Foo:
    @quux
    def bar(self, baz):
        pass


mock_foo = mock.create_autospec(Foo())
mock_foo.bar()

This works in python 2, but fails in python 3 with:

Traceback (most recent call last):
  File "thing.py", line 17, in <module>
    mock_foo.bar()
  File "./venv/lib/python3.6/site-packages/mock/mock.py", line 1061, in __call__
    _mock_self._mock_check_sig(*args, **kwargs)
  File "./venv/lib/python3.6/site-packages/mock/mock.py", line 209, in checksig
    sig.bind(*args, **kwargs)
  File "/usr/lib/python3.6/inspect.py", line 2934, in bind
    return args[0]._bind(args[1:], kwargs)
  File "/usr/lib/python3.6/inspect.py", line 2849, in _bind
    raise TypeError(msg) from None
TypeError: missing a required argument: 'baz'

Originally I tried using makefun and edit the signature to remove the baz parameter, since in my real setup, the value is always injected as the first parameter before the method.

I would like to be able to keep autospeccing my mocks to make sure that functions are called properly during tests. However, since I would like to run on python 3 at some point, this setup is untenable.

Side note: one of the roadblocks I ran into was I sometimes needed to wrap callable objects instead of functions, so my ideal solution works for those as well.

0 Answers
Related