【问题标题】:Mock a decorator function to bypass decorator logic模拟装饰器函数以绕过装饰器逻辑
【发布时间】:2020-10-01 12:13:05
【问题描述】:

我正在尝试为我的代码编写一些单元测试,而这些代码又使用了装饰器

import unittest
from unittest.mock import patch
from functools import wraps

def decorator(f):
    @wraps(f)
    def decorated(x):
        return f(x+1)
    return decorated

@decorator
def get_value(x):
    return x
    
class MyTestCase(unittest.TestCase):
    @patch('file.decorator', lambda f: f)
    def test_something(self):
        result = get_value(1)
        self.assertEqual(1, result)

我正在尝试模拟装饰函数以仅返回 f 并完全绕过装饰器逻辑部分。这在Overriding decorator during unit test in python 中有讨论,但实际上并没有用。

【问题讨论】:

  • decorator 在您的测试运行时已经被调用;它在定义get_value 后立即运行。嘲笑它为时已晚。

标签: python python-unittest


【解决方案1】:

由于装饰器在您定义get_value 后立即运行,因此模拟装饰器为时已晚。但是,您可以做的(因为您使用了functools.wraps)是模拟get_value 本身并以某种方式使用get_value.__wrapped__(原始函数)。类似的东西

@patch('tmp.get_value', get_value.__wrapped__)
def test_something(self):
    result = get_value(1)
    self.assertEqual(1, result)

(在这种情况下,我将经过上述更改的原始代码放在tmp.py 中,并将其作为python3 -munittest tmp.py 运行,因此我修补了引用tmp.get_value。)

如果您预计需要测试未修饰的原件,则将其保留在其自己的(私有)名称下进行测试可能更简单:无需修补。

import unittest
from functools import wraps

def decorator(f):
    @wraps(f)
    def decorated(x):
        return f(x+1)
    return decorated

def _get_value(x):
    return x

get_value = decorator(_get_value)
    
class MyTestCase(unittest.TestCase):
    def test_something(self):
        result = _get_value(1)
        self.assertEqual(1, result)

【讨论】:

  • 很好的答案。另外我想指出,单元测试应该只用于公共接口和稳定的概念。测试实现细节——例如测试一个包装的函数——可以限制所述代码的可变性,因为这些测试用例很容易被破坏。
  • 感谢您的回答!但是我提供的测试用例可能有点过于简单了。在我的用例中,装饰函数具有更复杂的逻辑需要运行。我想我需要在这里重新考虑我的方法
猜你喜欢
  • 2012-11-01
  • 2020-05-20
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 2020-05-31
  • 2010-10-27
  • 2011-06-06
  • 2011-06-25
相关资源
最近更新 更多