pytest-monkeypatch 一个装饰器(不使用模拟/补丁)

sol*_*977 7 python monkeypatching pytest

我正在使用 pytest 和 Monkeypatch 固定装置编写一些测试。按照规则,我导入类和方法以从它们正在使用的模块而不是源代码中模拟出来。

我正在为其编写测试的应用程序是一个使用标准环境的 Google App Engine 应用程序。因此我必须使用 python 2.7,我使用的实际版本是 2.7.15 - pytest 版本是 3.5.0

到目前为止,一切都运行良好,但在尝试模拟装饰器函数时遇到了问题。

从顶部开始。在一个名为decorators.py的py文件中包含所有auth装饰器,包括我想要模拟的装饰器。所讨论的装饰器是一个模块函数,而不是类的一部分。

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
Run Code Online (Sandbox Code Playgroud)

该装饰器应用于Web请求功能。名为 handlers (handlers.UserDetails) 的文件夹中名为 UserDetails.py 的文件中的基本示例

from decorators import user_login_required

class UserDetailsHandler(BaseHandler):
    @user_login_required
    def get(self):
        # Do web stuff, return html, etc
Run Code Online (Sandbox Code Playgroud)

在测试模块中,我设置测试如下:

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)
Run Code Online (Sandbox Code Playgroud)

问题在于,monkeypatch 不允许我将单个函数作为目标。它希望目标是一个类,后面是要替换的方法名称,然后是模拟方法......

monkeypatch.setattr(WouldBeClass, "user_login_required", mock_user_login_required_func)
Run Code Online (Sandbox Code Playgroud)

我尝试调整代码,看看是否可以通过更改装饰器的导入和使用方式来绕过它,如下所示:

import decorators

class UserDetailsHandler(BaseHandler):
    @decorators.user_login_required
    def get(self):
        # Do web stuff, return html, etc
Run Code Online (Sandbox Code Playgroud)

然后在测试中我尝试像这样修补函数名称......

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)
Run Code Online (Sandbox Code Playgroud)

尽管此代码不会引发任何错误,但当我单步执行测试时,代码永远不会进入mock_user_login_required_func。它总是进入现场装饰器。

我究竟做错了什么?这是尝试对装饰器进行一般性修补的问题,还是模块中的单独函数无法修补?

sou*_*ipe 2

看起来这里的快速答案就是简单地移动您的处理程序导入,以便它在补丁之后发生。装饰器和被装饰的函数必须位于单独的模块中,以便 python 在您修补它之前 \xe2\x80\x99 不会执行装饰器。

\n\n
from decorators import user_login_required\n\n@pytest.mark.parametrize(\'params\', get_params, ids=get_ids)\ndef test_post(self, params, monkeypatch):\n\n    monkeypatch.setattr(decorators, "user_login_required" , mock_user_login_required_func)\n    from handlers.UserDetails import UserDetailsHandler\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可能会发现使用内置的 unittest.mock 模块中的 patch 函数可以更轻松地完成此任务。

\n