Ответ 1
Из https://bitbucket.org/pytest-dev/pytest/issues/349/using-fixtures-in-pytestmarkparametrize может показаться, что в настоящее время невозможно использовать приборы в pytest.mark.parametrize
.
Рассмотрим следующий Pytest:
import pytest
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture
def timeline():
return TimeLine()
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
if __name__ == "__main__":
pytest.main([__file__])
В тесте test_timeline
используется прибор Pytest, timeline
, который сам имеет атрибут instances
. Этот атрибут повторяется в тесте, так что тест проходит, только если утверждение выполняется для каждого instance
в timeline.instances
.
@pytest.mark.parametrize("instance", timeline.instances)
def test_timeline(timeline):
assert instance % 2 == 0
но это приводит к
AttributeError: 'function' object has no attribute 'instances'
Как я понимаю, в приборах Pytest функция "становится" ее возвращаемым значением, но это, похоже, еще не произошло в то время, когда тест параметризуется. Как настроить тест по желанию?
Из https://bitbucket.org/pytest-dev/pytest/issues/349/using-fixtures-in-pytestmarkparametrize может показаться, что в настоящее время невозможно использовать приборы в pytest.mark.parametrize
.
Это возможно с помощью косвенной параметризации.
В этом примере выполняется то, что вы хотите с помощью pytest 3.1.2:
import pytest
class TimeLine:
def __init__(self, instances):
self.instances = instances
@pytest.fixture
def timeline(request):
return TimeLine(request.param)
@pytest.mark.parametrize(
'timeline',
([1, 2, 3], [2, 4, 6], [6, 8, 10]),
indirect=True
)
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
if __name__ == "__main__":
pytest.main([__file__])
Также см. этот похожий вопрос.
вместо косвенной параметризации или моего хакерского решения, приведенного ниже, включающего наследование, вы также можете просто использовать аргумент params
для @pytest.fixture()
- я думаю, что это, возможно, самое простое решение?
import pytest
class TimeLine:
def __init__(self, instances=[0, 0, 0]):
self.instances = instances
@pytest.fixture(params=[
[1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def timeline(request):
return TimeLine(request.param)
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures
Использование косвенной параметризации работает, но я нахожу необходимость использовать request.param
как магическую неназванную переменную, немного странную.
Вот образец, который я использовал. Это неловко по-другому, возможно, но, возможно, вы тоже предпочтете это!
import pytest
class TimeLine:
def __init__(self, instances):
self.instances = instances
@pytest.fixture
def instances():
return [0, 0, 0]
@pytest.fixture
def timeline(instances):
return TimeLine(instances)
@pytest.mark.parametrize('instances', [
[1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
фиксатор timeline
зависит от другого фиксатора, называемого instances
, который имеет значение по умолчанию [0,0,0]
, но в реальном тесте мы используем parametrize
чтобы ввести ряд различных значений для instances
.
На мой взгляд, преимущество в том, что у всего есть хорошее имя, плюс вам не нужно передавать этот indirect=True
флаг indirect=True
.
Это запутанная тема, потому что люди склонны думать, что приборы и параметры - это одно и то же. Они не собраны и даже не собраны на одной и той же стадии пробега. Конструктивны параметры предназначены быть собраны, когда тесты собираются (список тестовых узлов находится в стадии строительства), в то время как светильники предназначены для исполнения, когда тестовые узлы запуска (список тестовых узлов фиксирован и не может быть изменен). Таким образом, содержимое прибора не может использоваться для изменения количества тестовых узлов - это роль параметров. Смотрите также мой подробный ответ здесь.
Вот почему ваш вопрос не имеет решения "как есть": вы должны помещать экземпляры Timeline
шкалы в параметр, а не в фиксатор. Как это:
import pytest
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
def test_timeline(timeline):
assert timeline % 2 == 0
В ответе Курта Пика упоминается еще одна тема, которая, по-моему, добавляет здесь путаницу, поскольку она не имеет прямого отношения к вашему вопросу. Поскольку он упоминается и даже принимается в качестве решения, позвольте мне уточнить: действительно, в pytest вы не можете использовать осветитель в @pytest.mark.parametrize
. Но даже если бы ты мог, это ничего не изменит.
Поскольку эта функция теперь доступна в pytest-cases
(я автор), вы можете попробовать сами. Единственный способ заставить ваш пример работать даже с этой функцией - все тот же: удалить список из прибора. Итак, вы в конечном итоге:
import pytest
from pytest_cases import pytest_parametrize_plus, fixture_ref
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
@pytest_parametrize_plus("t", [fixture_ref(timeline)])
def test_timeline(t):
assert t % 2 == 0
То же самое, что и в предыдущем примере, с дополнительным, возможно, бесполезным слоем. Примечание: см. Также это обсуждение.
Вы параметризуете paramset, ссылающийся на функцию. Возможно, вы имеете в виду ссылку на Timeline.instances, а не на функцию фиксации /timeline/:
@pytest.mark.parametrize("instance", Timeline.instances)
def test_func(instance):
pass
Я думаю, что вы хотите добавить метку xfail к одному набору параметров, возможно, по какой-то причине?
def make_my_tests():
return [0, 2, mark.xfail(1, reason='not even')]
@mark.parametrize('timeline', paramset=make_my_tests(), indirect=True)
def test_func(timeline):
assert timeline % 2 == 0
Я решил создать функцию без декоратора fixture
и функции, которая является предыдущей функцией с примененным декоратором:
def one_raw():
return 1
one = pytest.fixture(one_raw)
def test_add(one):
assert one + 2 == 3
@pytest.mark.parametrize("subtrahend", (one_raw(), int(True)))
def test_sub(subtrahend):
assert 4 - subtrahend == 3