Как я могу повторить каждый тест несколько раз в прогоне py.test?

Я хочу запускать каждый выбранный элемент py.test произвольным числом раз, последовательно.
Я не вижу никакого стандартного механизма py.test для этого.

Я попытался сделать это в тэге pytest_collection_modifyitems(). Я изменил список переданных элементов, чтобы указать каждый элемент более одного раза. Первое выполнение тестового элемента работает так, как ожидалось, но это, похоже, вызывает некоторые проблемы для моего кода.

Кроме того, я бы предпочел иметь уникальный объект тестового элемента для каждого запуска, так как я использую id (item) в качестве ключа в различных сообщениях. К сожалению, я не могу найти код py.test для дублирования тестового элемента, copy.copy() не работает, а copy.deepcopy() получает исключение.

Может кто-нибудь предложить стратегию для выполнения теста несколько раз?

Ответ 1

Чтобы выполнить каждый тест несколько раз, мы будем программно параметризовать каждый тест по мере создания тестов.

Сначала добавьте параметр парсера (включите следующее в один из ваших файлов conftest.py):

def pytest_addoption(parser):
    parser.addoption('--repeat', action='store',
        help='Number of times to repeat each test')

Теперь добавим квест "pytest_generate_tests". Здесь происходит волшебство.

def pytest_generate_tests(metafunc):
    if metafunc.config.option.repeat is not None:
        count = int(metafunc.config.option.repeat)

        # We're going to duplicate these tests by parametrizing them,
        # which requires that each test has a fixture to accept the parameter.
        # We can add a new fixture like so:
        metafunc.fixturenames.append('tmp_ct')

        # Now we parametrize. This is what happens when we do e.g.,
        # @pytest.mark.parametrize('tmp_ct', range(count))
        # def test_foo(): pass
        metafunc.parametrize('tmp_ct', range(count))

Запуск без флага повторения:

(env) $ py.test test.py -vv
============================= test session starts ==============================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python
collected 2 items 

test.py:4: test_1 PASSED
test.py:8: test_2 PASSED

=========================== 2 passed in 0.01 seconds ===========================

Запуск с флагом повторения:

(env) $ py.test test.py -vv --repeat 3
============================= test session starts ==============================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python
collected 6 items 

test.py:4: test_1[0] PASSED
test.py:4: test_1[1] PASSED
test.py:4: test_1[2] PASSED
test.py:8: test_2[0] PASSED
test.py:8: test_2[1] PASSED
test.py:8: test_2[2] PASSED

=========================== 6 passed in 0.01 seconds ===========================

Дальнейшее чтение:

Ответ 2

Для этой цели существует pytest-модуль pytest-repeat, и я рекомендую использовать модули, где это возможно, вместо того, чтобы повторно выполнять свои функции самостоятельно.

Чтобы использовать его, просто добавьте pytest-repeat в requirements.txt или pip install pytest-repeat, затем выполните ваши тесты с помощью --count n.

Ответ 3

Одной из возможных стратегий является параметризация рассматриваемого теста, но явно не использование параметра.

Например:

@pytest.mark.parametrize('execution_number', range(5))
def run_multiple_times(execution_number):
    assert True

Вышеуказанный тест должен выполняться пять раз.

Проверьте документацию параметризации: https://pytest.org/latest/parametrize.html

Ответ 4

Основываясь на предположении Фрэнка Т, я нашел очень простое решение в выноске pytest_generate_tests():

parser.addoption ('--count', default=1, type='int', metavar='count', help='Run each test the specified number of times')

def pytest_generate_tests (metafunc):
    for i in range (metafunc.config.option.count):
        metafunc.addcall()

Теперь выполнение "py.test --count 5" заставляет каждый тест выполняться пять раз в тестовом сеансе.

И это не требует никаких изменений в наших существующих тестах.

Спасибо, Фрэнк Т!