Объединение отчетов о покрытии инструментов istanbul
и coverage
имеет две основные проблемы:
- Естественно,
coverage
ничего не знает о файлах javascript, поэтому мы должны как-то зарегистрировать их в прогоне покрытия - это будет сделано путем реализации пользовательского плагина.
- Мы должны преобразовать
istanbul
отчет о покрытии в формат coverage
понимает.
Настройка
Поскольку фрагмент кода был бы слишком большим, чтобы его можно было вставить непосредственно в ответ, я подготовил git-репозиторий , с которым вы можете воспроизвести тестовый прогон (конечно, вы можете повторно использовать код по своему усмотрению) :
$ git clone https://github.com/hoefling/stackoverflow-52124836
$ cd stackoverflow-52124836/
$ yarn install
Сначала создайте отчет о покрытии в Стамбуле:
$ yarn test
yarn run v1.9.4
warning package.json: No license field
$ istanbul cover _mocha js
Array
#length
✓ should be 0 when the array is empty
✓ should be 1 when the array has one element
✓ should be 2 when the array has two elements
Array
#indexOf()
✓ should return -1 when the value is not present
4 passing (5ms)
=============================================================================
Writing coverage object [/private/tmp/stackoverflow-52124836/coverage/coverage.json]
Writing coverage reports at [/private/tmp/stackoverflow-52124836/coverage]
=============================================================================
=============================== Coverage summary ===============================
Statements : 100% ( 14/14 )
Branches : 100% ( 0/0 )
Functions : 100% ( 8/8 )
Lines : 100% ( 14/14 )
================================================================================
Теперь запускайте тесты на Python с pytest
:
$ python -m pytest -sv --cov=py --cov=js --cov-report=term-missing
=================================== test session starts ===================================
platform darwin -- Python 3.6.4, pytest-3.7.3, py-1.5.4, pluggy-0.7.1 --
/Users/hoefling/.virtualenvs/stackoverflow/bin/python
cachedir: .pytest_cache
rootdir: /private/tmp/stackoverflow-52124836, inifile:
plugins: cov-2.5.1
collected 1 item
py/test_spam.py::test_spam PASSED
---------- coverage: platform darwin, python 3.6.4-final-0 -----------
Name Stmts Miss Cover Missing
-------------------------------------------------------
js/array.length.spec.js 14 0 100%
js/array.spec.js 8 0 100%
py/test_spam.py 2 0 100%
-------------------------------------------------------
TOTAL 24 0 100%
================================ 1 passed in 0.56 seconds =================================
js
/ py
dirs
Это только некоторые примеры тестовых файлов для воспроизведения. Чтобы упростить настройку, istanbul
собирает покрытие по тестовому коду.
mycov
Содержит плагин для coverage
. Для получения подробной информации о том, как писать плагины для coverage
, обратитесь к Классы плагинов ; здесь я просто объясняю соответствующие пятна:
class IstanbulPlugin(coverage.plugin.CoveragePlugin, coverage.plugin.FileTracer):
def file_reporter(self, filename):
return FileReporter(filename)
def file_tracer(self, filename):
return None
def find_executable_files(self, src_dir):
yield from (str(p) for p in pathlib.Path(src_dir).rglob('*.js')
if not any(d in p.parts for d in ('node_modules', 'coverage',)))
Класс плагина ничего не делает, кроме поиска файлов javascript и регистрации их как выполненных в coverage
run (метод find_executable_files
). Он не записывает покрытие кода вообще! Он также регистрирует простой файл-репортер для файлов JavaScript:
class FileReporter(coverage.plugin.FileReporter):
def source(self):
with open(self.filename) as fp:
js = fp.read()
return js
def lines(self):
return {i + 1 for i, line in enumerate(self.source().split(os.linesep)) if line.strip()}
Репортер возвращает исходный код файлов javascript как есть, исполняемые строки - это все строки кода, которые не являются пустыми.
Примечание 1:
Этого не достаточно! Например, строки комментария к строке и блоку будут считаться исполняемыми. Вам нужно будет адаптировать метод lines
; Лучше всего использовать синтаксический анализатор кода JavaScript, который извлекает информацию об исполняемых строках.
Примечание 2:
И istanbul
, и coverage
считают строки от 1, а не от 0, таким образом, смещение номеров строк.
Теперь вам нужно зарегистрировать плагин через coverage_init
:
# mycov/__init__.py
def coverage_init(reg, options):
reg.add_file_tracer(IstanbulPlugin())
и добавьте пользовательский плагин в .coveragerc
:
[run]
plugins = mycov
Добавить istanbul
отчет к coverage
Теперь, когда coverage
знает, что файлы javascript необходимо учитывать, мы объединяем покрытие js с покрытием python. За это отвечает прибор append_istanbul_coverage
в conftest.py
.
@pytest.fixture(autouse=True)
def append_istanbul_coverage(cov):
yield
with open('coverage/coverage.json') as fp:
data = json.load(fp)
converted = {'lines': {item['path']: line_numbers(item) for item in data.values()}}
text = "!coverage.py: This is a private format, don't read it directly!" + json.dumps(converted)
istanbul_cov = coverage.data.CoverageData()
with io.StringIO(text) as fp:
istanbul_cov.read_fileobj(fp)
cov.data.update(istanbul_cov)
Прибор будет автоматически выполняться один раз за тестовый прогон, код после yield
будет запускаться, когда все тесты будут выполнены. Приспособление cov
предоставляется pytest-cov
; мы используем его для доступа к текущему объекту данных coverage
. Сначала мы читаем покрытие istanbul
; затем преобразуйте его в строку, которая может быть понята как coverage
- это просто json
с добавленным специальным сообщением. После этого остается только обновить текущие coverage
данные, мы готовы!