Как pytest-cov может сообщить о покрытии кода Python, который выполняется в результате pexpect.spawn?
У меня есть проект Python, который использует pytest-cov для модульного тестирования и измерения покрытия кода.
Структура каталогов для моего проекта:
rift-python
+- rift # The package under test
| +- __init__.py
| +- __main__.py
| +- cli_listen_handler.py
| +- cli_session_handler.py
| +- table.py
| +- ...lots more...
+- tests # The tests
| +- test_table.py
| +- test_sys_2n_l0_l1.py
| +- ...more...
+- README.md
+- .travis.yml
+- ...
Я использую Трэвис, чтобы бежать pytest --cov=rift tests
для каждой регистрации, и я использую codecov для просмотра результатов покрытия кода.
Тестируемый пакет предлагает интерфейс командной строки (CLI), который читает команды из stdin и выдает выходные данные в stdout. Запускается как python rift
,
Каталог тестов содержит два типа тестов.
Первый тип тестов - это традиционные модульные тесты, которые тестируют отдельный класс. Например, test test_table.py импортирует table.py и выполняет традиционные тесты pytest (с использованием assert и т. Д.). Измерение покрытия кода работает, как и ожидалось для этих тестов: codecov точно сообщает, какие строки в пакете rift являются или не охватываются тестовое задание.
# test_table.py (codecov works)
import table
def test_simple_table():
tab = table.Table()
tab.add_row(['Animal', 'Legs'])
tab.add_rows([['Ant', 6]])
...
tab_str = tab.to_string()
assert (tab_str == "+--------+------+\n"
"| Animal | Legs |\n"
"+--------+------+\n"
"| Ant | 6 |\n"
"+--------+------+\n"
...
"+--------+------+\n")
Второй тип теста использует pexpect: он использует pexpect.spawn("python rift")
чтобы начать рифтовый пакет. Затем он использует pexpect.sendline
вводить команды в CLI (STDIN), и он использовал pexpect.expect
проверить вывод команд на CLI (стандартный вывод). Функциональность теста работает нормально, но кодеки не сообщают о покрытии кода для этих тестов.
# test_sys_2n_l0_l1.py (codecov does not pick up coverage of rift package)
# Greatly simplified example
import pexpect
def test_basic():
rift = pexpect.spawn("python rift")
rift.sendline("cli command")
rift.expect("expected output") # Throws exception if expected output not seen
ВОПРОС: Как я могу получить измерения покрытия кода, чтобы сообщить покрытую линию в пакете порожденного рифта для 2-го типа теста с использованием pexpect?
Примечание: я опустил несколько не относящихся к делу деталей, полный исходный код которых можно найти по адресу https://github.com/brunorijsman/rift-python (ОБНОВЛЕНИЕ: в этом репо теперь есть рабочее решение, предложенное в ответе)
2 ответа
Использование coverage run
запустить вашу программу pexpect и собрать данные:
Если вы обычно делаете:
pexpect.spawn("python rift")
Тогда вместо этого сделайте:
pexpect.spawn("coverage run rift.py")
( Источник)
После тестирования вы, вероятно, захотите объединить результаты pexpect с "обычными" результатами модульных тестов. coverage.py
Можно объединить несколько файлов в один для отчетности.
После того как вы создали несколько этих файлов, вы можете скопировать их все в один каталог и использовать combine
Команда объединить их в один .coverage
файл данных:
$ coverage combine
( Источник)
Две дополнительные детали из тестирования:
В тестовой программе (test_sys_2n_l0_l1.py) в этом примере вы должны убедиться, что у вас есть задержка между моментом, когда вы прекращаете порождение pexpect, и моментом, когда вы заканчиваете сам тест. В противном случае покрытие не успеет записать результаты в.coverage. Я добавил сон (1,0).
Используется "покрытие пробега - параллельный режим разлома". Это было необходимо для того, чтобы (а) убедиться, что.coverage не был перезаписан более поздними запусками, и (б) сделать работу "покрытия объединением" (которая автоматически запускается "pytest --cov")
В основном вы должны включить отслеживание покрытия подпроцесса.
Я рекомендую использовать https://pypi.org/project/coverage_enable_subprocess/ чтобы легко это включить.
С помощью parallel = 1
рекомендуется / требуется тогда, и вы должны экспортировать COVERAGE_PROCESS_START
например, export COVERAGE_PROCESS_START="$PWD/.coveragerc"
,