装饰器适用于函数但不适用于类
Posted
技术标签:
【中文标题】装饰器适用于函数但不适用于类【英文标题】:Decorator works for Function but not Class 【发布时间】:2018-12-06 16:10:41 【问题描述】:需要一些帮助来纠正单元测试的装饰器。我不确定如何满足单元测试的要求。 这个装饰器的想法是,如果传入 True,则将测试标记为 expectedFailure。否则允许测试简单地运行。这个装饰器适用于测试函数,但不适用于类定义 p>
import unittest
def expectedFailureIf(expFailure):
if expFailure:
return unittest.expectedFailure
return lambda func: func
@expectedFailureIf(GetCurrentOS() == kPlatMac) # Fails on Class
class someClass(unittest.TestCase):
#@expectedFailureIf(GetCurrentOS() == kPlatMac) # Works on Function
def test_sometestA(self):
assert True
def test_sometestB(self):
assert False
我得到的错误是 test_sometest() 正好需要 1 个参数。移除装饰器允许测试运行。将装饰器移动到函数顶部允许测试运行。
历史...我的一个平台运行良好,而另一个平台却不行。我想让一个平台运行所有测试,而另一个平台将被标记为预期失败。当然我不想使用skip或skip if。因为那将不允许有效平台运行。将它们标记为预期的失败也将不起作用,因为一个平台将返回意外的成功。使用 expectedFailureIf(),每个平台都会正确报告,一旦问题得到解决,这些测试将报告为意外成功。当事情得到解决时,它会通知我。对我来说……这似乎是一个更好的结果。
【问题讨论】:
你不需要使用== True
。这不是您遇到问题的原因,而是您需要做的所有事情。您的装饰器在其他方面与 one used by Python's own test suite 完全相同。
@wim:跳过与预期失败不是一回事。跳过意味着您不运行测试。预期失败运行测试然后断言测试失败(报告意外成功)。
SkipIf 可能具有相同的功能,但并不相同。我希望测试能够在两个平台上运行。我希望在修复当前失败的平台时收到通知。此外,我对其他用于 unittest 的装饰器有想法,这将使我更好地跟踪“应该”修复的事情。 UnitTest 装饰器有点限制。所以这是我想学习的东西。
好的,我现在明白了。您希望在修复后出现测试失败,并且测试在该平台上意外通过。相当复杂,但现在说得通了。
@wim: 和 skipIf()
条件装饰器的作用与此装饰器完全相同,因此如果 OP 对此装饰器有问题,那么 skipIf
也是一个问题。
【参考方案1】:
在 Python 3 上,您的代码运行良好。 implementation of unittest.expectedFailure
在那里更好,并且在装饰类或函数时可以正常工作。
在 Python 2 上,unittest.expectedFailure
is only designed to work on functions。
这是一个适用于 Python 2 的替代品。
import inspect
import types
import unittest
def expectedFailureIf(condition):
if callable(condition):
condition = condition()
if not condition:
# return identity function for no-op
return lambda x: x
def patch(func_or_class):
if isinstance(func_or_class, types.FunctionType):
return unittest.expectedFailure(func_or_class)
for name, member in inspect.getmembers(func_or_class):
if name.startswith('test') and isinstance(member, types.MethodType):
setattr(func_or_class, name, unittest.expectedFailure(member))
return func_or_class
return patch
@expectedFailureIf(True)
class MyTest(unittest.TestCase):
def test_that_passes(self):
assert 2 + 2 == 4
def test_that_fails(self):
assert 2 + 2 == 5
if __name__ == "__main__":
unittest.main(verbosity=2)
结果:
test_that_fails (__main__.MyTest) ... expected failure
test_that_passes (__main__.MyTest) ... unexpected success
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK (expected failures=1, unexpected successes=1)
警告!
意外成功并没有使测试运行失败这一事实是 Python 中的一个错误!它在 2014 年 1 月(Python 3.4)中得到解决,但由于向后兼容性问题,此错误修复未合并到 2.7 的分支中(请参阅issue20165 上的 cmets)。所以,不幸的是,现在是 Python 2 "feature"。
如果这对您不利,请考虑升级到更多recent Python version 和/或使用better test runner。
【讨论】:
您使用的是 Python 3.x 还是 2.7?我从您的代码中得到的只是:在 0.000 秒内运行 0 次测试 如果测试运行,测试应该返回“test_some_test (main.SomeClassTest) ...预期失败。”就在虚线和状态报告之前。所以测试没有运行。修改您的代码以在 If 条件下方包含打印“标记”:并用数学替换断言以断言 False。运行测试,发现装饰器被调用并被标记。不幸的是,测试本身没有运行。 错误,正在运行测试。放入打印声明或您自己查看的内容。 好的,这太棒了。完全适用于 Class 案例。将装饰器移到 pass 函数顶部显示: AttributeError: 'MyTest' object has no attribute 'name' with the Passed function with a test_that_passes (main.MyTest) 。 .. 输出顶部的 ERROR 语句。 太棒了!!这适用于类和函数调用。我相信,我了解您在这里所做的工作,并且应该能够编写一堆对我的团队有用的其他装饰器。以上是关于装饰器适用于函数但不适用于类的主要内容,如果未能解决你的问题,请参考以下文章