Py.test - 基于会话的设置

1 投票
2 回答
695 浏览
提问于 2025-04-17 05:46

我正在尝试在 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

撰写回答