I am writing some tests using pytest with the monkeypatch fixture. Following the rules I am importing the classes and methods to mock out from the module they are being used in and not from the source.
The application I am writing tests for is a Google App Engine application which uses the Standard environment. As such I have to use python 2.7, the actual version I am using is 2.7.15 - pytest version is 3.5.0
Everything has been working well so far but I have hit a problem when trying to mock out a decorator function.
Starting from the top. In a py file called decorators.py contains all the auth decorators including the decorator I want to mock out. The decorator in question is a module function, not part of a class.
def user_login_required(handler):
def is_authenticated(self, *args, **kwargs):
u = self.auth.get_user_by_session()
if u.access == '' or u.access is None:
# return the response
self.redirect('/admin', permanent=True)
else:
return handler(self, *args, **kwargs)
return is_authenticated
The decorator is applied to the web request function. A basic example in a file called UserDetails.py in a folder called handlers (handlers.UserDetails)
from decorators import user_login_required
class UserDetailsHandler(BaseHandler):
@user_login_required
def get(self):
# Do web stuff, return html, etc
In a test module I am setting up the test like this:
from handlers.UserDetails import user_login_required
@pytest.mark.parametrize('params', get_params, ids=get_ids)
def test_post(self, params, monkeypatch):
monkeypatch.setattr(user_login_required, mock_user_login_required_func)
The problem with this is that monkeypatch does not allow me to put a single function in as the target. It wants the target to be a Class, followed by the method name to be replaced then the mock method....
monkeypatch.setattr(WouldBeClass, "user_login_required", mock_user_login_required_func)
I have tried to adjust the code to see if I can get round it by changing how the decorator is imported and used like this:
import decorators
class UserDetailsHandler(BaseHandler):
@decorators.user_login_required
def get(self):
# Do web stuff, return html, etc
Then in the test I try to patch the function name like so.....
from handlers.UserDetails import decorators
@pytest.mark.parametrize('params', get_params, ids=get_ids)
def test_post(self, params, monkeypatch):
monkeypatch.setattr(decorators, "user_login_required" , mock_user_login_required_func)
Although this code does not throw any errors, when I step through the test the code never enters the mock_user_login_required_func. It always goes into the live decorator.
What am I doing wrong? This this a problem with trying to monkeypatch decorators in general or can lone functions in modules not be patched?