固定装置的pytest夹具

时间:2016-03-03 16:35:59

标签: python pytest

我目前正在为中型图书馆(~300个文件)编写测试。 该库中的许多类共享相同的测试方案,使用pytest编码:

文件test_for_class_a.py:

import pytest

@pytest.fixture()
def setup_resource_1():
    ...

@pytest.fixture()
def setup_resource_2():
    ...

@pytest.fixture()
def setup_class_a(setup_resource_1, setup_resource_2):
    ...

def test_1_for_class_a(setup_class_a):
    ...

def test_2_for_class_a(setup_class_a):
    ...

class_b,class_c等存在类似的文件......唯一的区别是setup_resource_1&的内容。 setup_resource_2。

现在我想重新使用在test_for_class_a.py,test_for_class_b.py和test_for_class_c.py中定义的fixtures setup_class_a,setup_class_b,setup_class_c来对它们进行测试。

在文件test_all_class.py中,这可行,但每次测试仅限于一个夹具:

from test_for_class_a import *

@pytest.mark.usefixtures('setup_class_a')      # Fixture was defined in test_for_class_a.py
def test_some_things_on_class_a(request)
    ...

但我正在寻找一种更通用的方法:

from test_for_class_a import *
from test_for_class_b import *   # I can make sure I have no collision here 
from test_for_class_c import *   # I can make sure I have no collision here 

==> @generate_test_for_fixture('setup_class_a', 'setup_class_b', 'setup_class_c') 
def test_some_things_on_all_classes(request)
    ...

有什么方法可以做一些接近它的事情吗? 我一直在寻找工厂和抽象pytest工厂的工厂,但我正在努力与pytest定义夹具的方式。 有没有办法解决这个问题?

3 个答案:

答案 0 :(得分:3)

我们在工作中遇到了同样的问题,我希望每个案例只能编写一次夹具。所以我编写了插件pytest-data来做到这一点。例如:

@pytest.fixture
def resource(request):
    resource_data = get_data(reqeust, 'resource_data', {'some': 'data', 'foo': 'foo'})
    return Resource(resource_data)

@use_data(resource_data={'foo': 'bar'})
def test_1_for_class_a(resource):
    ...

@use_data(resource_data={'foo': 'baz'})
def test_2_for_class_a(resource):
    ...

有什么好处的是你只用一些默认值来编写夹具。当您只需要该夹具/资源并且您不关心特定设置时,您只需使用它。当你需要测试一些特定的属性时,让我们说检查一下这个资源是否也可以处理100个字符的长值,你可以通过use_data装饰器传递它而不是写另一个装置。

因此你不必关心冲突,因为一切都只会在那里。然后,您可以在不导入测试模块的情况下将conftest.py用于所有灯具。例如,我们确实将所有灯具的深层模块分开,并将其全部包含在顶部conftest.py中。

插件pytest-data的文档:http://horejsek.github.io/python-pytest-data/

答案 1 :(得分:0)

我发现的一个解决方案是滥用测试用例如下:

from test_for_class_a import *
from test_for_class_b import *
from test_for_class_c import *

list_of_all_fixtures = []


# This will force pytest to generate all sub-fixture for class a
@pytest.mark.usefixtures(setup_class_a)
def test_register_class_a_fixtures(setup_class_a)
    list_of_fixtures.append(setup_class_a)


# This will force pytest to generate all sub-fixture for class b
@pytest.mark.usefixtures(setup_class_b)
def test_register_class_b_fixtures(setup_class_b)
    list_of_fixtures.append(setup_class_b)


# This will force pytest to generate all sub-fixture for class c
@pytest.mark.usefixtures(setup_class_c)
def test_register_class_b_fixtures(setup_class_c)
    list_of_fixtures.append(setup_class_c)


# This is the real test to apply on all fixtures
def test_all_fixtures():
    for my_fixture in list_of_all_fixtures:
        # do something with my_fixture

这隐含地依赖于在所有test_register_class *之后执行所有test_all_fixture的事实。它显然很脏但是有效......

答案 2 :(得分:0)

我认为,只有 pytest_generate_test() (example) 可以为您提供如此强大的自定义功能:

def pytest_generate_tests(metafunc):
    if 'db' in metafunc.funcargnames:
        metafunc.addcall(param="d1")
        metafunc.addcall(param="d2")

编辑:哎呀,回答了比我有 o.O 经验还早的问题