Py.test - 基于会话的设置
我正在尝试在 py.test 的基础上构建一个不错的框架。
我们的一些测试需要特定的测试数据才能正常工作。
目前,我们只是把一个模拟对象作为函数的参数,并在生成器中进行设置,这显然不是一个好方法。
以下是现在的一个例子:
def test_something(self, some_data):
# some_data is unused in the test
我想做成这样:
@uses_some_data
def test_something(self):
# The data is loaded when the test is run
不过我还没有找到正确的方法来实现这个。
我不能使用类的设置,因为我希望数据在整个会话中保持不变,而不是在每个测试类中都进行设置和拆除。
我最初的想法是继续使用 funcargs,但不是让测试直接使用 funcarg,而是让装饰器为函数请求 funcarg,这样就可以隐藏那些不太好看的部分。
但这样的问题是,我需要一个 py.test 对象来请求 funcarg。
有没有办法让我获得这样的对象,或者说这样的方法根本就是错的?
如果没有收集到的测试需要这些数据,数据就不需要加载,那就太好了,因为使用装饰器的缺点是它们总是会运行,无论测试是否会被执行。
2 个回答
1
这里有一些可能直接有效的内容,如果不行,希望能给你指明方向。
class TestData(object):
def __getattr__(self, name):
if name not in ('data1', 'data2', 'data3'):
raise AttributeError("TestData has no %s" % name)
if name == 'data1':
result = self._generate_data('data1')
setattr(self.__class__, name, result)
elif name == 'data2':
result = self._generate_data('data2')
setattr(self.__class__, name, result)
elif name == 'data3':
result = self._generate_data('data3')
setattr(self.__class__, name, result)
return result
def _generate_data(self, data_name):
return data_name * int(data_name[-1])
TestData类使用了一个叫做__getattr__
的方法,这个方法会在需要数据的时候生成数据。而且,它会把生成的数据保存回类里面(不是实例里!),这样以后还可以继续使用这些数据。
class uses_some_data(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
global test_data
test_data = TestData()
return self.func(*args, **kwargs)
这是一个简单的装饰器,用来设置test_data
的全局名称绑定。实际上,这个版本的装饰器非常简单,可以很容易地用test_data = TestData()
来替代。
@uses_some_data
def testing_test():
print(test_data.data2)
还有一个测试函数。
如果你不喜欢test_data
在全局的设置,你可以把装饰器做得更复杂,把test_data
绑定到函数本身:
class uses_some_data(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
self.func.test_data = TestData()
return self.func(*args, **kwargs)
在这种情况下,确保你的测试函数能够引用自己。
@uses_some_data
def testing_test():
print(testing_test.test_data.data2)
1
经过一番尝试,我发现这样做是有效的:
def pytest_funcarg__some_data(request):
def create():
# Load the test data here
print 'Test data loaded'
return request.cached_setup(
setup=create,
scope='session',
extrakey='some_data'
)
def uses_some_data(func):
# The funcarg is actually requested here
def wrapper(self, some_data):
return func
return wrapper
class TestSomething(object):
@uses_some_data
def test_something(self):
# "Some data" is now available
pass