如何在python中测试时删除装饰器的效果?

jbr*_*own 6 python testing mocking

retry在python的一些代码中使用了装饰器.但是我希望通过消除它的效果来加速我的测试.

我的代码是:

@retry(subprocess.CalledProcessError, tries=5, delay=1, backoff=2, logger=logger)
def _sftp_command_with_retries(command, pem_path, user_at_host):
    # connect to sftp, blah blah blah
    pass
Run Code Online (Sandbox Code Playgroud)

如何在测试时删除装饰器的效果?我无法创建未修饰的版本,因为我正在测试使用它的更高级别的函数.

由于retry用于time.sleep退出,理想情况下我可以修补,time.sleep但因为这是在装饰器中我不认为这是可能的.

有什么办法可以加快测试使用这个功能的代码吗?

更新

我基本上试图测试使用它的更高级别的函数,以确保它们捕获任何抛出的异常_sftp_command_with_retries.由于retry装饰器将传播它们,我需要一个更复杂的模拟.

所以从这里我可以看到如何模拟装饰器.但现在我需要知道如何编写一个本身就是装饰器的模拟器.它需要调用_sftp_command_with_retries,如果它引发异常,则传播它,否则返回返回值.

导入我的函数后添加它不起作用:

_sftp_command_with_retries = _sftp_command_with_retries.__wrapped__ 
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 7

retry您正在使用装饰器构建在decorator.decorator实用程序装饰器之上,如果未安装该包,则具有更简单的回退.

结果有一个__wrapped__属性,使您可以访问原始函数:

orig = _sftp_command_with_retries.__wrapped__
Run Code Online (Sandbox Code Playgroud)

如果decorator未安装您在3.2之前使用的是Python版本,则该属性将不存在; 你必须手动进入装饰器闭包:

orig = _sftp_command_with_retries.__closure__[1].cell_contents
Run Code Online (Sandbox Code Playgroud)

(索引0处的闭包是retry_decorator在调用retry()自身时产生的).

请注意,decorator它在retry程序包元数据中列为依赖项,如果您安装它,则会自动安装pipdecorator程序包.

可以支持用两种可能性try...except:

try:
    orig = _sftp_command_with_retries.__wrapped__
except AttributeError:
    # decorator.decorator not available and not Python 3.2 or newer.
    orig = _sftp_command_with_retries.__closure__[1].cell_contents
Run Code Online (Sandbox Code Playgroud)

请注意,您始终可以time.sleep()使用模拟进行修补.装饰器代码将使用模拟,因为它引用模块源代码中的"全局" time模块.

或者,您可以修补retry.api.__retry_internal:

import retry.api
def dontretry(f, *args, **kw):
    return f()

with mock.patch.object(retry.api, '__retry_internal', dontretry):
    # use your decorated method
Run Code Online (Sandbox Code Playgroud)

这暂时替换了直接调用原始函数的实际重试功能.