Выполнение скриптов в тестовой функции
Нет ничего плохого в том, чтобы вызывать скрипты из тестовой функции, так что ваш подход идеально подходит. Однако я бы использовал параметризацию вместо запуска скриптов в цикле for; Таким образом, вы получите хороший результат выполнения теста один раз за сценарий. Если вам не нравятся длинные трассировки, вы можете вырезать их в пользовательском pytest_exception_interact
hookimpl. Пример:
# conftest.py
def pytest_exception_interact(node, call, report):
excinfo = call.excinfo
if 'script' in node.funcargs:
excinfo.traceback = excinfo.traceback.cut(path=node.funcargs['script'])
report.longrepr = node.repr_failure(excinfo)
Параметризованные тесты:
# test_spam.py
import pathlib
import runpy
import pytest
scripts = pathlib.Path(__file__, '..', 'scripts').resolve().glob('*.py')
@pytest.mark.parametrize('script', scripts)
def test_script_execution(script):
runpy.run_path(script)
Результаты выполнения теста (для тестирования я создал простые сценарии с одной строкой, например assert False
или 1 / 0
:
$ pytest -v
======================================= test session starts ========================================
platform linux -- Python 3.6.8, pytest-4.6.3, py-1.8.0, pluggy-0.12.0 -- /home/hoefling/projects/.venvs/stackoverflow/bin/python3.6
cachedir: .pytest_cache
rootdir: /home/hoefling/projects/private/stackoverflow/so-56807698
plugins: mock-1.10.4, cov-2.7.1, forked-1.0.2, xdist-1.28.0, django-3.4.8
collected 3 items
test_spam.py::test_script_execution[script0] PASSED
test_spam.py::test_script_execution[script1] FAILED
test_spam.py::test_script_execution[script2] FAILED
============================================= FAILURES =============================================
____________________________________ test_script_runpy[script1] ____________________________________
> assert False
E AssertionError
scripts/script_3.py:1: AssertionError
____________________________________ test_script_runpy[script2] ____________________________________
> 1 / 0
E ZeroDivisionError: division by zero
scripts/script_2.py:1: ZeroDivisionError
================================ 2 failed, 1 passed in 0.07 seconds ================================
Пользовательский протокол испытаний
Если вам не нравится вышеуказанное решение, я могу подумать еще о том, чтобы реализовать собственный протокол сбора и выполнения тестов. Пример:
# conftest.py
import pathlib
import runpy
import pytest
def pytest_collect_file(parent, path):
p = pathlib.Path(str(path))
if p.suffix == '.py' and p.parent.name == 'scripts':
return Script(path, parent)
class Script(pytest.File):
def collect(self):
yield ScriptItem(self.name, self)
class ScriptItem(pytest.Item):
def runtest(self):
runpy.run_path(self.fspath)
def repr_failure(self, excinfo):
excinfo.traceback = excinfo.traceback.cut(path=self.fspath)
return super().repr_failure(excinfo)
Он будет собирать каждый файл .py
в каталоге scripts
, оборачивать каждый сценарий в тестовый пример и вызывать runpy
при выполнении теста. Журнал выполнения будет выглядеть примерно одинаково, только тесты будут называться по-разному.