Python:如何在单元(鼻子)测试期间忽略装饰器?

Posted

技术标签:

【中文标题】Python:如何在单元(鼻子)测试期间忽略装饰器?【英文标题】:Python: How to ignore decorators during unit (nose) tests? 【发布时间】:2018-06-05 23:26:51 【问题描述】:

我想忽略我的代码中的某些装饰器以跳过相关的功能,但仅在作为单元测试的一部分运行时。这个可以吗?

例如,我有一个函数 f,使用 numba 装饰器定义如下:

@numba.jit
def f(a, b):
  return a + b

当我运行调用上述函数的单元测试时,我不希望 numba 魔法发生,因为它会减慢速度并在某些平台上导致错误。是否可以在某个地方进行设置,告诉鼻子在不应用任何 numba 即时(和/或所有 numba)装饰器的情况下运行测试?

【问题讨论】:

我不知道,我不知道它会如何工作;我想你必须自己写。例如,您可以使用f = numba.jit(f) 表单在模块中的更高级别公开该函数,然后直接在您的测试中导入原始的解包版本。 【参考方案1】:

仅适用于 numba,您可以设置环境变量(例如 export NUMBA_DISABLE_JIT=1)以使 jit 装饰器无操作。

http://numba.pydata.org/numba-doc/dev/user/troubleshoot.html#disabling-jit-compilation

【讨论】:

这个标志会在进程级别禁用 JIT,对吧? 你会在哪里指定export NUMBA_DISABLE_JIT=1?我在我的项目存储库中添加了一个 .numba_config.yaml 文件,但这会全局禁用 NUMBA,这不是我想要的。我只想为 pytest 会话禁用 NUMBA(或至少只为覆盖率报告禁用)。你有什么想法吗? @merowinger。这可能已经晚了,但是对于以后阅读此内容的任何人,您可以在 conftest.py 中指定 os.environ["NUMBA_DISABLE_JIT"] = "1" 并且它可以工作。【参考方案2】:

您也许可以欺骗鼻子使用您自己的装饰器来保持功能不变。 编写一个包含此函数的文件numba.py

def jit(func):
    return func

并放置在您的 PYTHONPATH 中。 要么从这个文件所在的目录运行你的测试,要么在 您的测试文件作为第一行:

import sys

sys.path.insert(0, 'path/to/dir/with/myfile') 

或者,您可以将环境变量PYTHONPATH 设置为您的文件所在的目录。

窗户:

set PYTHONPATH=%PYTHONPATH%;path/to/dir/with/myfile

Unix/Mac:

export PYTHONPATH=$PYTHONPATH$:path/to/dir/with/myfile

【讨论】:

为我的测试工作。 是的,它可能不适用于混合了装饰器和其他东西的库。但是对于numba,这是一个很好的解决方案。也许对于其他图书馆,它会类似于module.decorator = lambda f:f【参考方案3】:

你可以用你可以控制的新装饰器来修补装饰器:

import functools

def patch(parent, obj_name, switch_reg, switch_name):
    def gen_switcher():
        def wrapper(func):
            ori_wrapped = ori_decorator(func)
            @functools.wraps(func)
            def _(*args, **kwargs):
                if switch_reg.get(switch_name, False):
                    func_to_call = func
                else:
                    func_to_call = ori_wrapped
                print(func_to_call)
                return func_to_call(*args, **kwargs)
            return _
        return wrapper
    ori_decorator = getattr(parent, obj_name)
    setattr(parent, obj_name, gen_switcher())

与:

# have to patch the decorator before applying it
import numba
switchs = 
patch(numba, 'jit', switchs, 'DISABLE_NUMBA_JIT')

@numba.jit
def f(a, b):
  return a + b

f(1, 2)

产量:

CPUDispatcher(<function f at 0x10a5f90d0>)

然后用:

# this part could be rewrited as a context manager    
switchs['DISABLE_NUMBA_JIT'] = True
f(1, 2)

得到:

<function f at 0x10a5f90d0>

【讨论】:

这比我想进入的更神奇,因为看起来有更简单的解决方案,但是“好样的!”尽管如此。感谢您的帮助。 @JamesAdams 我没想到你的问题可以通过简单的谷歌和阅读文档来解决。【参考方案4】:

我会将函数分成一个非装饰部分进行单元测试——然后让你的真实函数具有装饰器并简单地调用辅助函数:

@numba.jit
def f(a, b):
  return f_undecorated(a, b)

def f_undecorated(a, b):
  return a + b

仅为f_undecorated编写单元测试。

【讨论】:

这是一种有趣/聪明的方法,很好,因为它使您无需对环境中的任何东西进行胡思乱想,一切都在代码中。然而,还有更多的代码/复杂性(实际上是名义上的),所以就是这样。不知道它与上面对我有用的答案相比如何(请告知)。无论如何感谢您的帮助,感谢您的努力。【参考方案5】:

如果您使用coverage,您可以通过在coverage 命令之前添加NUMBA_DISABLE_JIT=1 来修改您的CI .yml 文件:

NUMBA_DISABLE_JIT=1 coverage run -p "test_*.py"

【讨论】:

以上是关于Python:如何在单元(鼻子)测试期间忽略装饰器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 中对装饰器工厂输入进行单元测试

根据条件应用不同的装饰器

以下哪个装饰器用于在使用鼻子时将用户定义的设置和拆卸功能分配给测试功能?

python覆盖模块可以有条件地忽略单元测试中的行吗?

Python单元测试--使用装饰器实现测试跳过和预期故障

python unittest框架装饰器