【问题标题】:Py.test - Session based setupPy.test - 基于会话的设置
【发布时间】:2011-11-07 02:38:06
【问题描述】:

我正在尝试围绕 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。
有什么办法可以得到这样的对象,或者这是错误的方法吗?

如果收集到的测试都不需要数据,则不必加载数据将是一个很棒的奖励,这是使用装饰器的缺点,因为无论测试是否运行,它们总是运行不是。

【问题讨论】:

  • 不确定我是否正确理解了您的问题,可能我遗漏了一些东西。在您的第一个变体“def test_something(self, some_data): ...”中,如果函数体中不需要它,为什么还要在参数中指定“some_data”? funcargs 的要点是,只有在实际需要它们时才指定它们——就像普通的 Python 函数一样——只有在实际使用它们时才需要参数。
  • 我们需要基于会话的设置,只有在任何收集的测试需要它们时才会加载。
  • 测试函数的 funcarg 只有在执行测试时才会被实例化。在 funcarg 工厂中,您可以使用 cached_setup 来管理每个会话的资源,请参阅pytest.org/latest/…。希望这会有所帮助。
  • 是的,这就是我们今天使用的。我的目标是不使用仅用于设置的 funcargs。

标签: python decorator pytest


【解决方案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)

【讨论】:

    【解决方案2】:

    在玩了一些之后,我发现这是可行的:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-04
      • 1970-01-01
      • 1970-01-01
      • 2012-07-26
      • 2023-01-09
      • 2014-06-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多