Python 将参数传递给fixture函数

Python 将参数传递给fixture函数,python,fixtures,pytest,Python,Fixtures,Pytest,我正在使用py.test测试python类MyTester中包装的一些DLL代码。 为了进行验证,我需要在测试期间记录一些测试数据,然后进行更多处理。因为我有很多测试题。。。我希望在大多数测试中重用tester对象创建(MyTester实例)的文件 由于tester对象是获取DLL变量和函数引用的对象,我需要为每个测试文件将DLL变量列表传递给tester对象(要记录的变量与测试文件相同)。 清单的内容应用于记录规定的数据 我的想法是这样做: import pytest class MyTes

我正在使用py.test测试python类MyTester中包装的一些DLL代码。 为了进行验证,我需要在测试期间记录一些测试数据,然后进行更多处理。因为我有很多测试题。。。我希望在大多数测试中重用tester对象创建(MyTester实例)的文件

由于tester对象是获取DLL变量和函数引用的对象,我需要为每个测试文件将DLL变量列表传递给tester对象(要记录的变量与测试文件相同)。 清单的内容应用于记录规定的数据

我的想法是这样做:

import pytest

class MyTester():
    def __init__(self, arg = ["var0", "var1"]):
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

# located in conftest.py (because other test will reuse it)

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester()
    return _tester

# located in test_...py

# @pytest.mark.usefixtures("tester") 
class TestIt():

    # def __init__(self):
    #     self.args_for_tester = ["var1", "var2"]
    #     # how to pass this list to the tester fixture?

    def test_tc1(self, tester):
       tester.dothis()
       assert 0 # for demo purpose

    def test_tc2(self, tester):
       tester.dothat()
       assert 0 # for demo purpose
有没有可能做到这一点,或者有没有更优雅的方式

通常,我可以使用某种设置函数(xUnit风格)对每个测试方法进行设置。但我想获得某种重用。有人知道这在固定装置上是否可行吗

我知道我可以做这样的事情:(从文件中)

但是我需要直接在测试模块中进行参数化。
是否可以从测试模块访问夹具的params属性?

您可以从夹具功能访问请求的模块/类/功能(因此也可以从测试仪类访问),请参阅。因此,您可以在类或模块上声明一些参数,并且测试设备可以获取这些参数。

更新:由于这是这个问题的公认答案,而且有时仍然会被更新,因此我应该添加一个更新。虽然我最初的答案(如下)是在pytest的旧版本中实现这一点的唯一方法,因为pytest现在支持夹具的间接参数化。例如,您可以这样做(通过@imiric):

然而,尽管这种形式的间接参数化是明确的,如@Yukihiko Shinoda,它现在支持一种形式的隐式间接参数化(尽管我在官方文件中找不到任何明显的参考):

我不知道这个表单的确切语义是什么,但似乎
pytest.mark.parametize
认识到,尽管
test\u tc1
方法不接受名为
tester\u arg
的参数,但它所使用的
tester
夹具会接受,因此,它通过
tester
fixture传递参数化参数


我有一个类似的问题——我有一个名为
test\u package
的fixture,后来我希望在特定测试中运行该fixture时能够向该fixture传递一个可选参数。例如:

@pytest.fixture()
def test_package(request, version='1.0'):
    ...
    request.addfinalizer(fin)
    ...
    return package
(出于这些目的,夹具的功能或返回的
包的对象类型无关紧要)

然后,希望在测试函数中以某种方式使用这个fixture,这样我还可以为该fixture指定
version
参数,以用于该测试。这在目前是不可能的,尽管这可能是一个很好的特性

同时,很容易让我的fixture返回一个函数,该函数完成fixture之前所做的所有工作,但允许我指定
version
参数:

@pytest.fixture()
def test_package(request):
    def make_test_package(version='1.0'):
        ...
        request.addfinalizer(fin)
        ...
        return test_package

    return make_test_package
现在我可以在我的测试函数中使用它,如:

def test_install_package(test_package):
    package = test_package(version='1.1')
    ...
    assert ...
等等

OP尝试的解决方案朝着正确的方向发展,正如@hpk42所建议的那样,
MyTester.\uuuu init\uuuu
可以存储对请求的引用,如:

class MyTester(object):
    def __init__(self, request, arg=["var0", "var1"]):
        self.request = request
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"
然后使用此工具实现夹具,如:

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester(request)
    return _tester

如果需要,
MyTester
类可以稍微重新构造,以便在创建后更新其
.args
属性,以调整单个测试的行为。

py.test via实际上支持这一点

在您的情况下,您应该:

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [['var1', 'var2']], indirect=True)
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
稍微改进一下:解决这个问题的另一个优雅的方法是创建“参数装置”。我个人更喜欢它,而不是
pytest
indirect
特性。此功能可从获得,最初的想法是由提出的

请注意,
pytest案例
还提供了
@fixture
,允许您直接在设备上使用参数化标记,而不必使用
@pytest.fixture(params=…)


@使用_cases
参数化_,允许您从“case functions”中获取参数,这些“case functions”可以分组在一个类中,甚至单独的模块中。有关详细信息,请参阅。顺便说一句,我是作者;)

我找不到任何文档,但是,它似乎在最新版本的pytest中工作

@pytest.fixture
def测试仪(测试仪参数):
“”“创建测试程序对象”“”
返回MyTester(测试仪参数)
类别测试:
@pytest.mark.parametize('tester_arg',[['var1','var2']]))
def测试_tc1(自身,测试仪):
测试员
断言1

我制作了一个有趣的装饰器,允许编写如下装置:

import pytest

class MyTester():
    def __init__(self, arg = ["var0", "var1"]):
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

# located in conftest.py (because other test will reuse it)

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester()
    return _tester

# located in test_...py

# @pytest.mark.usefixtures("tester") 
class TestIt():

    # def __init__(self):
    #     self.args_for_tester = ["var1", "var2"]
    #     # how to pass this list to the tester fixture?

    def test_tc1(self, tester):
       tester.dothis()
       assert 0 # for demo purpose

    def test_tc2(self, tester):
       tester.dothat()
       assert 0 # for demo purpose
@fixture\u获取参数
def狗(请求,/,姓名,年龄=69):
返回f“{name}年龄为{age}的狗”
在这里,
/
左侧有其他装置,右侧有使用以下装置提供的参数:

@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
    assert dog == "Buddy the dog aged 7"
这与函数参数的工作方式相同。如果不提供
age
参数,将使用默认的
69
。如果不提供
名称
,或省略
dog.arguments
装饰符,则会得到常规的
类型错误:dog()缺少1个必需的位置参数:“name”
。如果有另一个fixture接受参数
name
,则它与此fixture不冲突

还支持异步装置

此外,这为您提供了一个很好的设置计划:

$ pytest test_dogs_and_owners.py --setup-plan

SETUP    F dog['Buddy', age=7]
...
SETUP    F dog['Champion']
SETUP    F owner (fixtures used: dog)['John Travolta']
一个完整的例子:

从插件导入夹具获取参数
@fixture_taking_参数
def狗(请求,/,姓名,年龄=69):
返回f“{name}年龄为{age}的狗”
@fixture_taking_参数
def所有者(请求,狗,/,name=“John Doe”):
产生f{name},owne
@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester(request)
    return _tester
@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [['var1', 'var2']], indirect=True)
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
import pytest
from pytest_cases import param_fixture

# create a single parameter fixture
var = param_fixture("var", [['var1', 'var2']], ids=str)

@pytest.fixture
def tester(var):
    """Create tester object"""
    return MyTester(var)

class TestIt:
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
from pytest_cases import fixture, parametrize

@fixture
@parametrize("var", [['var1', 'var2']], ids=str)
def tester(var):
    """Create tester object"""
    return MyTester(var)
@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
    assert dog == "Buddy the dog aged 7"
$ pytest test_dogs_and_owners.py --setup-plan

SETUP    F dog['Buddy', age=7]
...
SETUP    F dog['Champion']
SETUP    F owner (fixtures used: dog)['John Travolta']