Фильтрация фикстур 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.

Другие вопросы по тегам