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.
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.
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?
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?
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:
@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.
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.
In the meantime it was easy enough to make my fixture simply return a function that does all the work the fixture previously did, but allows me to specify the version
argument:
@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 answer suggests, the MyTester.__init__
could just store off a reference to the request like:
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.
This is actually supported natively in py.test via indirect parametrization.
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
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.
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.
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 doc for details. I'm the author by the way ;)
来源:https://stackoverflow.com/questions/18011902/pass-a-parameter-to-a-fixture-function