pytest
имеет плагин pytester
, созданный для тестирования самого pytest
и плагинов; тесты выполняются изолированно, что не влияет на текущий тест. Пример:
# conftest.py
import pytest
pytest_plugins = ['pytester']
@pytest.fixture
def spam(request):
yield request.param
У прибора spam
есть проблема, что он будет работать только с параметризованными тестами; как только он будет запрошен в непараметризованном тесте, он поднимет AttributeError
. Это означает, что мы не можем проверить его с помощью обычного теста, подобного следующему:
def test_spam_no_params(spam):
# too late to verify anything - spam already raised in test setup!
# In fact, the body of this test won't be executed at all.
pass
Вместо этого мы выполняем тест в изолированном тестовом прогоне, используя приспособление testdir
, которое предоставляется плагином pytester
:
import pathlib
import pytest
# an example on how to load the code from the actual test suite
@pytest.fixture
def read_conftest(request):
return pathlib.Path(request.config.rootdir, 'conftest.py').read_text()
def test_spam_fixture(testdir, read_conftest):
# you can create a test suite by providing file contents in different ways, e.g.
testdir.makeconftest(read_conftest)
testdir.makepyfile(
"""
import pytest
@pytest.mark.parametrize('spam', ('eggs', 'bacon'), indirect=True)
def test_spam_parametrized(spam):
assert spam in ['eggs', 'bacon']
def test_spam_no_params(spam):
assert True
""")
result = testdir.runpytest()
# we should have two passed tests and one failed (unarametrized one)
result.assert_outcomes(passed=3, error=1)
# if we have to, we can analyze the output made by pytest
assert "AttributeError: 'SubRequest' object has no attribute 'param'" in ' '.join(result.outlines)
Другой удобной возможностью загрузки тестового кода для тестов является метод testdir.copy_example
. Настройте корневой путь в pytest.ini
, например:
[pytest]
pytester_example_dir = samples_for_fixture_tests
norecursedirs = samples_for_fixture_tests
Теперь создайте файл samples_for_fixture_tests/test_spam_fixture/test_x.py
с содержанием:
import pytest
@pytest.mark.parametrize('spam', ('eggs', 'bacon'), indirect=True)
def test_spam_parametrized(spam):
assert spam in ['eggs', 'bacon']
def test_spam_no_params(spam):
assert True
(это тот же код, который был передан в виде строки testdir.makepyfile
ранее). Приведенный выше тест изменяется на:
def test_spam_fixture(testdir, read_conftest):
testdir.makeconftest(read_conftest)
# pytest will now copy everything from samples_for_fixture_tests/test_spam_fixture
testdir.copy_example()
testdir.runpytest().assert_outcomes(passed=3, error=1)
Таким образом, вам не нужно поддерживать код Python как строку в тестах, а также можно повторно использовать существующие тестовые модули, запустив их с pytester
. Вы также можете настроить корни тестовых данных с помощью знака pytester_example_path
:
@pytest.mark.pytester_example_path('fizz')
def test_fizz(testdir):
testdir.copy_example('buzz.txt')
будет искать файл fizz/buzz.txt
относительно корневого каталога проекта.
Для большего количества примеров обязательно ознакомьтесь с разделом Тестирование плагинов в pytest
документах; также, вы можете найти мой другой ответ на вопрос Как проверить, вызывает ли устройство Pytest исключение? полезно, так как оно содержит еще один рабочий пример для темы. Я также счел очень полезным изучить Testdir
код напрямую, к сожалению, pytest
не предоставляет для этого подробных документов, но код в значительной степени самодокументируется.