Фильтрация фикстур pytest
Это в основном то же самое, что и более старый вопрос, но есть надежда, что теперь есть лучшее решение.
Проблема: учитывая параметризованный прибор, как можно параметризовать тестовую функцию с помощью подмножества объектов приборов?
Пример:
import pytest
@pytest.fixture(params=range(7))
def square(request):
return request.param ** 2
def test_all_squares(square):
sqrt = square ** 0.5
assert sqrt == int(sqrt)
@pytest.fixture()
def odd_square(square):
if square % 2 == 1:
return square
pytest.skip()
def test_all_odd_squares(odd_square):
assert odd_square % 2 == 1
sqrt = odd_square ** 0.5
assert sqrt == int(sqrt)
Выход:
$ pytest pytests.py -v=========================================== ======================== запускается тестовая сессия ======================= ============================================...собрано 14 предметовpytests.py::test_all_squares[0] ПРОЙДЕН [ 7%] pytests.py::test_all_squares[1] ПРОШЕЛ [ 14%] pytests.py::test_all_squares[2] ПРОЙДЕН [ 21%] pytests.py::test_all_squares[3] ПРОЙДЕН [ 28%] pytests.py::test_all_squares[4] ПРОЙДЕН [ 35%] pytests.py::test_all_squares[5] ПРОЙДЕН [ 42%] pytests.py::test_all_squares[6] ПРОШЕЛ [ 50%] pytests.py::test_all_odd_squares[0] ПРОПУЩЕНО [ 57%] pytests.py::test_all_odd_squares[1] ПРОШЕЛ [ 64%] pytests.py::test_all_odd_squares[2] ПРОПУЩЕН [ 71%] pytests.py::test_all_odd_squares[3] ПРОШЕЛ [ 78%] pytests.py::test_all_odd_squares[4] ПРОПУЩЕНО [ 85%] pytests.py::test_all_odd_squares[5] ПРОШЕЛ [ 92%] pytests.py::test_all_odd_squares[6] ПРОПУЩЕН [100%] ============================================================== 10 пройдено, 4 пропущено через 0,02 с ============================== ================================
Хотя это работает, это не идеально:
- Он создает фиктивные тестовые примеры, которые всегда пропускаются
- Для этого требуются тестовые функции, которые используют отфильтрованный прибор, чтобы дать другое имя (
odd_square
) к параметру.
Вместо этого я бы хотел, например,
filter_fixture(predicate, fixture)
функция, которая фильтрует исходные объекты прибора и может быть передана в
pytest.mark.parametrize
, что-то вроде этого:
@pytest.mark.parametrize("square", filter_fixture(lambda x: x % 2 == 1, square))
def test_all_odd_squares(square):
assert square % 2 == 1
sqrt = square ** 0.5
assert sqrt == int(sqrt)
Возможно ли это как-то?
1 ответ
Если вам требуется определенная логика, чтобы определить, какие параметры применять к каждому тесту, вы можете рассмотреть возможность использования ловушки.
Функция ловушки вызывается для каждого собранного теста. В
metafunc
Аргумент позволяет динамически параметризовать каждый отдельный тестовый пример. Переписываем свой пример для использования
pytest_generate_tests
может выглядеть так:
def pytest_generate_tests(metafunc):
square_parameters = (x**2 for x in range(7))
if 'square' in metafunc.fixturenames:
metafunc.parametrize("square", square_parameters)
if 'odd_square' in metafunc.fixturenames:
odd_square_parameters = (x for x in square_parameters if x % 2 == 1)
metafunc.parametrize("odd_square", odd_square_parameters)
def test_all_squares(square):
sqrt = square ** 0.5
assert sqrt == int(sqrt)
def test_all_odd_squares(odd_square):
assert odd_square % 2 == 1
sqrt = odd_square ** 0.5
assert sqrt == int(sqrt)
Это приводит к запуску следующих тестовых случаев:
$ pytest -v pytests.py
=========== test session starts ===========
…
collected 10 items
pytests.py::test_all_squares[0] PASSED [ 10%]
pytests.py::test_all_squares[1] PASSED [ 20%]
pytests.py::test_all_squares[4] PASSED [ 30%]
pytests.py::test_all_squares[9] PASSED [ 40%]
pytests.py::test_all_squares[16] PASSED [ 50%]
pytests.py::test_all_squares[25] PASSED [ 60%]
pytests.py::test_all_squares[36] PASSED [ 70%]
pytests.py::test_all_odd_squares[1] PASSED [ 80%]
pytests.py::test_all_odd_squares[9] PASSED [ 90%]
pytests.py::test_all_odd_squares[25] PASSED [100%]
=========== 10 passed in 0.03s ===========
Обратите внимание, что идентификаторы тестов в моем примере немного отличаются от ваших. Однако вы можете предоставить явные идентификаторы теста, используя
ìds
аргумент
metafunc.parametrize
.