Как 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",

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