Обновление: поскольку это принятый ответ на этот вопрос, и он по-прежнему иногда получает одобрение, я должен добавить обновление. Хотя мой первоначальный ответ (ниже) был единственным способом сделать это в более старых версиях 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
Однако, хотя эта форма косвенной параметризации является явной, как отмечает @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
Я не знаю точно, какова семантика этой формы, но похоже, что она pytest.mark.parametrize
распознает, что, хотя test_tc1
метод не принимает аргумент с именем tester_arg
, его использует tester
прибор, который он использует, поэтому он передает параметризованный аргумент через tester
прибор.
У меня была аналогичная проблема - у меня вызывается прибор test_package
, и позже я хотел иметь возможность передавать необязательный аргумент этому приспособлению при его запуске в определенных тестах. Например:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
(Для этих целей не имеет значения, что делает прибор или какой тип возвращаемого объекта package
).
Тогда было бы желательно каким-то образом использовать это приспособление в тестовой функции таким образом, чтобы я мог также указать version
аргумент этого приспособления для использования с этим тестом. В настоящее время это невозможно, хотя это может быть хорошей функцией.
Тем временем было достаточно легко заставить мой прибор просто возвращать функцию, которая выполняет всю работу, которую ранее выполняла прибор, но позволяет мне указать 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.__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"
Затем используйте это, чтобы реализовать приспособление, например:
@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 через косвенную параметризацию .
В вашем случае у вас будет:
источник
indirect
ключевому слову аргумента заведомо скудна и недружелюбна, что , вероятно , объясняет неизвестность этой важной техники. Я несколько раз просматривал сайт py.test в поисках этой самой функции - только чтобы оказаться пустым, старым и сбитым с толку. Горечь - это место, известное как непрерывная интеграция. Спасибо Odin за Stackoverflow.test_tc1
становитсяtest_tc1[tester0]
.indirect=True
передаёт параметры всем вызываемым приборам, верно? Потому что в документации явно указаны приборы для косвенной параметризации, например, для прибора с именемx
:indirect=['x']
Вы можете получить доступ к запрашивающему модулю / классу / функции из функций фикстуры (и, следовательно, из вашего класса тестера), см. Взаимодействие с запросом тестового контекста из функции фикстуры . Таким образом, вы можете объявить некоторые параметры в классе или модуле, и прибор тестера сможет их подобрать.
источник
@fixture def my_fixture(request)
а затем@pass_args(arg1=..., arg2=...) def test(my_fixture)
и получать эти аргументыmy_fixture()
вот такarg1 = request.arg1, arg2 = request.arg2
. Возможно ли что-то подобное в py.test сейчас?Я не смог найти ни одного документа, однако, похоже, он работает в последней версии pytest.
источник
Nadège
был отменен. Таким образом, эта недокументированная функция (я думаю, что она все еще недокументирована?) Все еще существует.Чтобы немного улучшить ответ imiric : еще один элегантный способ решить эту проблему - создать « фиксации параметров». Я лично предпочитаю это
indirect
функцииpytest
. Эта функция доступна от Sup3rGeopytest_cases
, а первоначальная идея была предложена .Обратите внимание, что это
pytest-cases
также обеспечивает,@pytest_fixture_plus
что позволяет вам использовать метки параметризации на ваших осветительных приборах, и@cases_data
что позволяет вам получать параметры из функций в отдельном модуле. Подробности см. В документе . Кстати, я автор;)источник
param_fixture
. Смотрите этот ответ . Однако я не нашел в документации подобного примера; ты что-нибудь знаешь об этом?Я сделал забавный декоратор, который позволяет писать такие приспособления:
Здесь слева от
/
вас есть другие приборы, а справа у вас есть параметры, которые предоставляются с использованием:Это работает так же, как аргументы функции. Если вы не укажете
age
аргумент,69
вместо него будет использоваться значение по умолчанию ,,. если вы не предоставитеname
или опуститеdog.arguments
декоратор, вы получите обычныйTypeError: dog() missing 1 required positional argument: 'name'
. Если у вас есть другой прибор, который принимает аргументыname
, он не конфликтует с этим.Также поддерживаются асинхронные фикстуры.
Кроме того, это дает вам хороший план настройки:
Полный пример:
Код декоратора:
источник