Fork me on GitHub
2/10/2006

Покрытие кода тестами

Юнит-тесты хороши, когда покрывают весь ключевой код. Однако если проект чуть больше, чем "Hello, world!", то оценить степень покрытия весьма проблематично. Существуют инструменты, которые помогают это делать. Для Python я знаю два таких: Приведу примеры использования каждого из инструментов.

twisted.trial

У команды trial есть опция coverage, после выполнения всех тестов во временном каталоге тестов появляется информация о покрытии кода тестами (по умолчанию искать нужно в _trial_temp/coverage). Имена файлов имеют вид package_name.module_name.cover. Каждый файл содержит строчки кода и счетчик, сколько раз каждая строка была выполнена. Ни разу не выполненные строки помечаются маркером >>>>>>. Т.е. никаких количественных оценок twisted.trial не дает, лишь визуально выделяет непротестированные участки кода. Правда, получить количественные оценки по этим результатам достаточно просто (достаточно подсчитать общее количество "помеченных" и "посчитанных" строк и взять от этого количества процентное соотношение к "помеченным"), странно что это не сделали разработчики. Еще один минус, который хочу отметить - необходимо использовать twisted.trial, а не unittest (т.е. во всех модулях юнит-тестов заменить import unittest на from twisted.trial import unittest). Ну и ставить целиком Twisted (а trial является его неразрывным компонентом) только ради того, чтобы померить покрытие кода, нецелесообразно.

coverage.py

coverage.py является более адекватным инструментом - ни от каких дополнительных библиотек он не зависит, может использоваться как из командной строки, так и из кода (как библиотека). Плюс к этому, он может выдавать как суммарный результат (в процентах), так и анотированный (т.е. копия исходного файла, в котором протестированная строка помечается >, не протестированная - !). В общем, must have. Ниже приведу пару советов по использованию.

Из командной строки

Первый шаг - собирается информация по ходу выполнения юнит-тестов, собранная информация сохраняется в файле .coverage текущего каталога. Если ранее coverage.py уже запускался, то лучше сбросить ранее сохраненные данные (ключ -e)
$ coverage.py -e
$ coverage.py -x /path/to/unit/test_module.py
Второй шаг - по полученной информации создается суммарный отчет для указанного файла
$ coverage.py -r -m /path/to/module.py
Если же хочется получить анотированный отчет, то для этого служит опция -a:
$ coverage.py -a /path/to/module.py
и после этого, рядом с module.py появляется анотированный module.py,cover Ну и "живой" пример с PyTils:
$ coverage.py -e
$ coverage.py -x /usr/local/lib/python2.4/site-packages/pytils/test/__init__.py
testChoosePlural (pytils.test.test_numeral.ChoosePluralTestCase) ... ok
[...]
testProvideUnicode (pytils.test.test_utils.UnicodeTestCase) ... ok

----------------------------------------------------------------------
Ran 36 tests in 0.252s

OK
$ coverage.py -r -m /usr/local/lib/python2.4/site-packages/pytils/numeral.py 
Name                                                    Stmts   Exec  Cover   Missing
-------------------------------------------------------------------------------------
/usr/local/lib/python2.4/site-packages/pytils/numeral     138    136    98%   340, 365

Из кода

Использовать coverage.py из кода даже проще, чем из командной строки:
>>> import coverage
>>> coverage.erase()
>>> coverage.start()
>>> import yourmodule
>>> youmodule.test.run()
>>> coverage.stop()
>>> coverage.report([yourmodule.foo, yourmodule.bar])
и живой пример:
>>> import coverage
>>> coverage.erase()
>>> coverage.start()
>>> import pytils
>>> pytils.VERSION
'0.1.0-svn20061002'
>>> pytils.test.run()
....................................
----------------------------------------------------------------------
Ran 36 tests in 0.207s

OK
>>> coverage.stop()
>>> coverage.report([pytils.dt, pytils.numeral, pytils.translit, pytils.utils])
Name              Stmts   Exec  Cover   Missing
-----------------------------------------------
pytils.dt            85     85   100%   
pytils.numeral      138    136    98%   340, 365
pytils.translit      38     37    97%   197
pytils.utils         34     32    94%   28-29
-----------------------------------------------
TOTAL               295    290    98%
P.S. Нед говорит, что еще есть pycover и что в Sancho тоже можно покрытие кода померять, но я ни тот, ни другой не пробовал.

Комментарии

Все статьи