在python中进行测试时,如何删除装饰器的效果? [英] How to remove the effects of a decorator while testing in python?

查看:257
本文介绍了在python中进行测试时,如何删除装饰器的效果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在python中的某些代码中使用retry装饰器.但是我想通过消除其影响来加快测试速度.

I'm using the retry decorator in some code in python. But I want to speed up my tests by removing its effect.

我的代码是:

@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

在测试时如何去除装饰器的效果?我无法创建未修饰的版本,因为我正在测试使用该版本的高级功能.

How can I remove the effect of the decorator while testing? I can't create an undecorated version because I'm testing higher-level functions that use this.

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

Since retry uses time.sleep to back off, ideally I'd be able to patch time.sleep but since this is in a decorator I don't think that's possible.

有什么办法可以加快使用此功能的代码的测试速度?

Is there any way I can speed up testing code that uses this function?

更新

我基本上是在尝试测试使用此功能的更高级别的函数,以确保它们捕获到_sftp_command_with_retries引发的所有异常.由于retry装饰器将传播它们,因此我需要一个更复杂的模拟.

I'm basically trying to test my higher-level functions that use this to make sure that they catch any exceptions thrown by _sftp_command_with_retries. Since the retry decorator will propagate them I need a more complicated mock.

因此来自此处我可以看到如何模拟装饰器.但是现在我需要知道如何编写一个本身就是装饰器的模拟程序.它需要调用_sftp_command_with_retries,如果它引发了异常,请传播该异常,否则返回返回值.

So from here I can see how to mock a decorator. But now I need to know how to write a mock that is itself a decorator. It needs to call _sftp_command_with_retries and if it raises an exception, propagate it, otherwise return the return value.

在导入我的功能后添加此功能无效:

Adding this after importing my function didn't work:

_sftp_command_with_retries = _sftp_command_with_retries.__wrapped__ 

推荐答案

您正在使用的 retry装饰器构建在 decorator.decorator实用程序装饰器的顶部,并且如果未安装该软件包,则回退更为简单

The retry decorator you are using is built on top of the decorator.decorator utility decorator with a simpler fallback if that package is not installed.

结果具有__wrapped__属性,可让您访问原始功能:

The result has a __wrapped__ attribute that gives you access to the original function:

orig = _sftp_command_with_retries.__wrapped__

如果未安装decorator ,并且您使用的是3.2之前的Python版本,则该属性将不存在.您将需要手动进入装饰器的关闭位置:

If decorator is not installed and you are using a Python version before 3.2, that attribute won't be present; you'd have to manually reach into the decorator closure:

orig = _sftp_command_with_retries.__closure__[1].cell_contents

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

(the closure at index 0 is the retry_decorator produced when calling retry() itself).

请注意,decoratorretry软件包元数据中被列为依赖项,并且如果您将其与pip一起安装,则会自动安装decorator软件包.

Note that decorator is listed as a dependency in the retry package metadata, and if you installed it with pip the decorator package would have been installed automatically.

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

You can support both possibilities with a 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

请注意,您总是可以使用模拟补丁对time.sleep()进行修补.装饰器代码将在引用time模块时使用该模拟程序>模块源代码.

Note that you always can patch time.sleep() with a mock. The decorator code will use the mock as it references the 'global' time module in the module source code.

或者,您可以使用以下方法修补retry.api.__retry_internal:

Alternatively, you could patch retry.api.__retry_internal with:

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

with mock.patch.object(retry.api, '__retry_internal', dontretry):
    # use your decorated method

这会将执行实际重试的功能临时替换为直接调用原始功能的功能.

This temporarily replaces the function that does the actual retrying with one that just calls your original function directly.

这篇关于在python中进行测试时,如何删除装饰器的效果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆