Python 将参数传递给夹具函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18011902/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pass a parameter to a fixture function
提问by maggie
I am using py.test to test some DLL code wrapped in a python class MyTester. For validating purpose I need to log some test data during the tests and do more processing afterwards. As I have many test_... files I want to reuse the tester object creation (instance of MyTester) for most of my tests.
我正在使用 py.test 来测试一些包含在 Python 类 MyTester 中的 DLL 代码。出于验证目的,我需要在测试期间记录一些测试数据,然后进行更多处理。由于我有很多 test_... 文件,我想在大多数测试中重用测试器对象创建(MyTester 的实例)。
As the tester object is the one which got the references to the DLL's variables and functions I need to pass a list of the DLL's variables to the tester object for each of the test files (variables to be logged are the same for a test_... file). The content of the list is shall be used to log the specified data.
由于测试对象是获得对 DLL 变量和函数的引用的对象,因此我需要将 DLL 变量列表传递给每个测试文件的测试对象(要记录的变量对于 test_.. 。 文件)。列表的内容应用于记录指定的数据。
My idea is to do it somehow like this:
我的想法是以某种方式这样做:
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
Is it possible to achieve it like this or is there even a more elegant way?
有可能像这样实现它还是有更优雅的方式?
Usually I could do it for each test method with some kind of setup function (xUnit-style). But I want to gain some kind of reuse. Does anyone know if this is possible with fixtures at all?
通常我可以使用某种设置函数(xUnit 样式)为每个测试方法执行此操作。但我想获得某种重用。有谁知道这是否可以通过固定装置实现?
I know I can do something like this: (from the docs)
我知道我可以做这样的事情:(来自文档)
@pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"])
But I need to the parametrization directly in the test module. Is it possible to access the params attribute of the fixture from the test module?
但是我需要直接在测试模块中进行参数化。 是否可以从测试模块访问夹具的 params 属性?
采纳答案by Iguananaut
Update:Since this the accepted answer to this question and still gets upvoted sometimes, I should add an update. Although my original answer (below) was the only way to do this in older versions of pytest as othershave notedpytest now supports indirect parametrization of fixtures. For example you can do something like this (via @imiric):
更新:由于这是对这个问题的公认答案,并且有时仍然会得到支持,我应该添加一个更新。尽管我的原始答案(如下)是在旧版本的 pytest 中执行此操作的唯一方法,因为其他人已经指出pytest 现在支持设备的间接参数化。例如,您可以执行以下操作(通过 @imiric):
# test_parameterized_fixture.py
import pytest
class MyTester:
def __init__(self, x):
self.x = x
def dothis(self):
assert self.x
@pytest.fixture
def tester(request):
"""Create tester object"""
return MyTester(request.param)
class TestIt:
@pytest.mark.parametrize('tester', [True, False], indirect=['tester'])
def test_tc1(self, tester):
tester.dothis()
assert 1
$ pytest -v test_parameterized_fixture.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items
test_parameterized_fixture.py::TestIt::test_tc1[True] PASSED [ 50%]
test_parameterized_fixture.py::TestIt::test_tc1[False] FAILED
However, although this form of indirect parametrization is explicit, as @Yukihiko Shinoda points outit now supports a form of implicit indirect parametrization (though I couldn't find any obvious reference to this in the official docs):
但是,尽管这种间接参数化形式是明确的,但正如@Yukihiko Shinoda指出的那样,它现在支持一种隐式间接参数化形式(尽管我在官方文档中找不到任何明显的参考资料):
# test_parameterized_fixture2.py
import pytest
class MyTester:
def __init__(self, x):
self.x = x
def dothis(self):
assert self.x
@pytest.fixture
def tester(tester_arg):
"""Create tester object"""
return MyTester(tester_arg)
class TestIt:
@pytest.mark.parametrize('tester_arg', [True, False])
def test_tc1(self, tester):
tester.dothis()
assert 1
$ pytest -v test_parameterized_fixture2.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items
test_parameterized_fixture2.py::TestIt::test_tc1[True] PASSED [ 50%]
test_parameterized_fixture2.py::TestIt::test_tc1[False] FAILED
I don't know exactly what are the semantics of this form, but it seems that pytest.mark.parametrize
recognizes that although the test_tc1
method does not take an argument named tester_arg
, the tester
fixture that it's using does, so it passes the parametrized argument on through the tester
fixture.
我不确切知道这种形式的语义是什么,但它似乎pytest.mark.parametrize
认识到虽然该test_tc1
方法不接受名为 的参数,但它使用tester_arg
的tester
夹具确实如此,因此它通过tester
夹具传递参数化参数。
I had a similar problem--I have a fixture called test_package
, and I later wanted to be able to pass an optional argument to that fixture when running it in specific tests. For example:
我有一个类似的问题——我有一个名为 的夹具test_package
,后来我希望能够在特定测试中运行它时将可选参数传递给该夹具。例如:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
(It doesn't matter for these purposes what the fixture does or what type of object the returned package
) is.
(对于这些目的,夹具的作用或返回的对象类型无关紧要package
)。
It would then be desirable to somehow use this fixture in a test function in such a way that I can also specify the version
argument to that fixture to use with that test. This is currently not possible, though might make a nice feature.
然后希望以某种方式在测试函数中使用这个夹具,这样我也可以指定该version
夹具的参数以用于该测试。这目前是不可能的,但可能会成为一个不错的功能。
In the meantime it was easy enough to make my fixture simply return a functionthat does all the work the fixture previously did, but allows me to specify the version
argument:
与此同时,很容易让我的夹具简单地返回一个函数,该函数完成夹具之前所做的所有工作,但允许我指定version
参数:
@pytest.fixture()
def test_package(request):
def make_test_package(version='1.0'):
...
request.addfinalizer(fin)
...
return test_package
return make_test_package
Now I can use this in my test function like:
现在我可以在我的测试函数中使用它,例如:
def test_install_package(test_package):
package = test_package(version='1.1')
...
assert ...
and so on.
等等。
The OP's attempted solution was headed in the right direction, and as @hpk42's answersuggests, the MyTester.__init__
could just store off a reference to the request like:
OP 尝试的解决方案朝着正确的方向发展,正如@hpk42 的回答所暗示的那样,它MyTester.__init__
可以只存储对请求的引用,例如:
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"
Then use this to implement the fixture like:
然后使用它来实现夹具,如:
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester(request)
return _tester
If desired the MyTester
class could be restructured a bit so that its .args
attribute can be updated after it has been created, to tweak the behavior for individual tests.
如果需要,MyTester
可以对类进行一些重构,以便.args
在创建后更新其属性,以调整各个测试的行为。
回答by hpk42
You can access the requesting module/class/function from fixture functions (and thus from your Tester class), see interacting with requesting test context from a fixture function. So you could declare some parameters on a class or module and the tester fixture can pick it up.
您可以从夹具函数(因此从您的测试器类)访问请求模块/类/函数,请参阅与来自夹具函数的请求测试上下文交互。因此,您可以在类或模块上声明一些参数,并且测试夹具可以选择它。
回答by imiric
This is actually supported natively in py.test via indirect parametrization.
这实际上是通过间接参数化在 py.test 中原生支持的。
In your case, you would have:
在您的情况下,您将拥有:
@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
回答by smarie
To improve a little bit imiric's answer: another elegant way to solve this problem is to create "parameter fixtures". I personally prefer it over the indirect
feature of pytest
. This feature is available from pytest_cases
, and the original idea was suggested by Sup3rGeo.
为了改善一点imiric 的答案:解决这个问题的另一种优雅方法是创建“参数装置”。我个人更喜欢它在indirect
的功能pytest
。此功能可从 获得pytest_cases
,最初的想法是由Sup3rGeo提出的。
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
Note that pytest-cases
also provides @pytest_fixture_plus
that allow you to use parametrization marks on your fixtures, and @cases_data
that allow you to source your parameters from functions in a separate module. See docfor details. I'm the author by the way ;)
请注意,pytest-cases
它还提供@pytest_fixture_plus
了允许您在灯具上使用参数化标记的功能,并@cases_data
允许您从单独模块中的函数中获取参数。有关详细信息,请参阅文档。顺便说一下,我是作者;)
回答by Yukihiko Shinoda
I couldn't find any document, however, it seems to work in latest version of pytest.
我找不到任何文件,但是,它似乎在最新版本的 pytest 中工作。
@pytest.fixture
def tester(tester_arg):
"""Create tester object"""
return MyTester(tester_arg)
class TestIt:
@pytest.mark.parametrize('tester_arg', [['var1', 'var2']])
def test_tc1(self, tester):
tester.dothis()
assert 1