作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我最近尝试在单元测试最佳实践方面对自己进行大量培训。其中大部分都非常有道理,但有一点经常被忽视和/或解释不当:一个单元测试修饰函数应该如何?
假设我有这段代码:
def stringify(func):
@wraps(func)
def wrapper(*args):
return str(func(*args))
return wrapper
class A(object):
@stringify
def add_numbers(self, a, b):
"""
Returns the sum of `a` and `b` as a string.
"""
return a + b
我显然可以编写以下测试:
def test_stringify():
@stringify
def func(x):
return x
assert func(42) == "42"
def test_A_add_numbers():
instance = MagicMock(spec=A)
result = A.add_numbers.__wrapped__(instance, 3, 7)
assert result == 10
这给了我 100% 的覆盖率:我知道任何用 stringify()
修饰的函数都会得到字符串形式的结果,而且我知道未修饰的 A.add_numbers()
函数返回其参数的总和。因此,根据传递性,A.add_numbers()
的装饰版本必须返回其参数的总和,作为字符串。一切似乎都很好!
但是我对此并不完全满意:如果我使用另一个装饰器(做其他事情,比如将结果乘以 2 而不是转换为 海峡
)。我的函数 A.add_numbers
将不再正确,但测试仍会通过。不太好。
我可以测试 A.add_numbers()
的装饰版本,但我会过度测试,因为我的装饰器已经过单元测试。
感觉好像我在这里遗漏了什么。对装饰函数进行单元测试的好策略是什么?
最佳答案
我最终将装饰器一分为二。因此,与其拥有:
def stringify(func):
@wraps(func)
def wrapper(*args):
return str(func(*args))
return wrapper
我有:
def to_string(value):
return str(value)
def stringify(func):
@wraps(func)
def wrapper(*args):
return to_string(func(*args))
return wrapper
这允许我稍后在测试装饰函数时简单地模拟 to_string
。
很明显,在这个简单的例子中,它可能看起来有些矫枉过正,但是当用在一个实际做一些复杂或昂贵的事情的装饰器上时(比如打开一个到数据库的连接,或其他),能够模拟它是一个非常好的
关于python - 如何对装饰函数进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30327518/
我是一名优秀的程序员,十分优秀!