我希望能够以类似于以下的格式编写一堆测试:
class TestPytest:
@given(3)
@expect(3)
def test_passes(self, g, e):
assert g == e
@given(3)
@expect(4)
def test_fails(self, g, e):
assert g == e
def test_boring(self): # for comparison
pass
(我不相信这是一个好主意,但我会在更进一步的方向上采取它,所以它并不像看起来那么奇怪。)
为此,我试图编写这些装饰器:
import functools
class WrappedTest(object):
def __init__(self, f):
self.func = f
self.given = []
self.expects = []
def __get__(self, instance, owner):
@functools.wraps(self.func)
def call(*a, **kw):
return self.func(instance, self.given, self.expects,
*a, **kw)
return call
def given(*objects):
def wrapped(test):
if not isinstance(test, WrappedTest):
test_tmp = WrappedTest(test)
test = functools.update_wrapper(test_tmp, test)
test.given.extend(objects)
return test
return wrapped
def expect(*objects):
def wrapped(test):
if not isinstance(test, WrappedTest):
test_tmp = WrappedTest(test)
test = functools.update_wrapper(test_tmp, test)
test.expects.extend(objects)
return test
return wrapped
但是当我尝试运行此测试时,pytest找不到test_passes
或test_fails
。它找到了test_boring
。
我的工作假设是我没有正确包装测试方法。它们显示为函数而不是方法:
>>> test_pytest.TestPytest().test_fails
<function test_pytest.test_fails>
>>> test_pytest.TestPytest().test_boring
<bound method TestPytest.test_boring of <test_pytest.TestPytest instance at 0x101f3dab8>>
但我不确定如何解决这个问题。我已经尝试将functools.wraps(self.func)
更改为functools.wraps(self.func.__get__(instance, owner))
,理论上它将使用绑定方法而不是函数进行包装。但这是一种猜测,它没有用。
我知道pytest能够找到正确编写的装饰函数,所以大概我做错了什么,但我不确定是什么。
答案 0 :(得分:1)
看起来我错了包装。通过pytest源,它以不同于方法的方式处理嵌套类。它通过__dict__
访问会员,忽略__get__
,因此WrappedTest
并未成功假装成方法。
我已经用一个函数替换了WrappedTest
实例,它似乎工作正常(即使没有@functools.wraps
行):
import functools
from collections import namedtuple
def wrap_test_method(meth):
if hasattr(meth, '_storage'):
return meth
Storage = namedtuple('Storage', ['given', 'expects'])
sto = Storage(given=[], expects=[])
@functools.wraps(meth)
def new_meth(self, *a, **kw):
return meth(self, sto.given, sto.expects, *a, **kw)
new_meth._storage = sto
return new_meth
def given(*objects):
def decorator(test_method):
new_test_method = wrap_test_method(test_method)
new_test_method._storage.given.extend(objects)
return new_test_method
return decorator
def expect(*objects):
def decorator(test_method):
new_test_method = wrap_test_method(test_method)
new_test_method._storage.expects.extend(objects)
return new_test_method
return decorator