В этой заметке расскажу о своем опыте юнит-тестирования JS-кода, опыте использования среды выполнения тестов js-test-driver, ее возможности code coverage и скручивании ежа с ужом, а именно данных о code coverage от js-test-driver и генератора отчетов о покрытии PHP_CodeCоverage. Расскажу и покажу как получить вот такие отчеты о покрытии кода...
Итак, потребовалось реализовать юнит-тестирование для JS-кода. В качестве среды для выполнения и фреймворка для написания тестов был выбран js-test-driver. Причины таковы:
Несмотря на то что плагин для IDE сейчас неработоспособен, «пощупать» технологию можно из консоли. Сервер и среда исполнения запускаются отдельно из консоли и могут гонять тесты «в ручном режиме» по пинку пользователя.
Код, который будем тестировать
И тест
Файловая структура
Конфигурация для запуска (файл conf в формате YAML)
Сперва стартуем сервер
Запускаем браузер, идем на localhost:4224, овладеваем браузером. Запускаем прогон тестов.
UPD: подключить к тестированию можно произвольное количество произвольных браузеров. Можно даже подключиться с удаленной машины из-под другой ОС. Подключение браузера к тестированию == открытие страницы на неком сервере (который в данном случае — вам сервер js-test-driver)
Видим, что прогнали всего 2 теста. В т.ч. в браузере Chrome — 1, и он прошел успешно, в браузере Safari — также 1 и также успешно. Все замечательно.
CodeCoverage подключается отдельным плагином. Данные о покрытии могут либо отображаться в виде статической информации (файл такой-то покрыт на N%) по окончании исполнения тестов, либо могут выгружаться в файл формата LCOV. Авторы предлагают генерировать визуальные отчеты с помощью тулзы genhtml. Беглый поиск портированных под Win32 результатов не дал, поднимать Cygwin или отдельную машину для построения отчетов не хочется...
Подключим плагин. Отредактируем конфигурационный файл (conf).
Запустим тесты
Видим, что дополнительно к результатам появилась информация о покрытии кода. От такого отчета толку чуть более чем никакого. Начнем сохранять результаты тестов в файл.
Теперь информации о покрытии не видно совсем, но в папке ./out появился файл с покрытием в формате LCOV.
Формат lcov-файла, генерируемого js-test-driver предельно прост.
SF — файл, для которого приводятся данне далее, DA — данные о покрытии (DA: Строка, СколькоРазВыполнена).
PHPUnit — фреймворк дле реализации юнит-тестирования для PHP, имеет возможность генерировать отчеты о code coverage. Модуль, занимающийся CodeCoverage, очень легко отделяем и очень аккуратно реализован. В состав входит интерфейс PHP_CodeCoverage_Driver, классы, имплементирующие его, могут служить источником данных о Code coverage для прочих компонентов проекта (построитель отчетов в т.ч.).
Для файла...
Получим результат...
Видно что форматы очень похожи, можно сделать свой драйвер
Ниже — простой пример кода, генерирующего отчет о покрытии. Предполагается, что данные о покрытии находятся в файле coverage.dat. Отчет будет расположен в папке CodeCoverageReport.
Что получается, можно посмотреть здесь. Можно посмотреть на отчет и увидеть, что наш сложный пример не полностью покрыт тестами, пропущена одна ветка и ее надо срочно покрыть тестами.
Итак, потребовалось реализовать юнит-тестирование для JS-кода. В качестве среды для выполнения и фреймворка для написания тестов был выбран js-test-driver. Причины таковы:
- есть в виде плагина для применяемой командой IDE — PhpStorm (к сожалению в настоящий момент плагин не работает на текущей платформе PhpStorm, о чем есть соответствующий тикет в статусе Started)
- умеет выполнять тесты автоматически в нескольких браузерах
- работает из командной строки, просто встроить в CI
- умеет давать отчеты о code coverage
Несмотря на то что плагин для IDE сейчас неработоспособен, «пощупать» технологию можно из консоли. Сервер и среда исполнения запускаются отдельно из консоли и могут гонять тесты «в ручном режиме» по пинку пользователя.
Попробуем в деле
Код, который будем тестировать
var greeter = function(toSay){
this.whatToSay = toSay;
}
greeter.prototype.say = function(sayBye){
if(sayBye == true)
return "Goodbye " + this.whatToSay;
else
return "Hello " + this.whatToSay;
}
И тест
var testCase = new TestCase("Say");
testCase.prototype.testCase1 = function(){
var i = new greeter('test');
assertEquals("Hello test", i.say(false));
};
Файловая структура
\jstd
\plugins
coverage.jar
code.js
test.js
conf
jstestdriver.jar
Конфигурация для запуска (файл conf в формате YAML)
load:
- code.js
- test.js
server: http://localhost:4224
Запускаем
Сперва стартуем сервер
H:\jstd>java -jar jstestdriver.jar --port 4224
Запускаем браузер, идем на localhost:4224, овладеваем браузером. Запускаем прогон тестов.
UPD: подключить к тестированию можно произвольное количество произвольных браузеров. Можно даже подключиться с удаленной машины из-под другой ОС. Подключение браузера к тестированию == открытие страницы на неком сервере (который в данном случае — вам сервер js-test-driver)
H:\jstd>java -jar JsTestDriver.jar --config conf --tests all
..
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (1,00 ms)
Chrome 6.0.472.63 Windows: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (1,00 ms)
Safari 525.28.1 Windows: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (1,00 ms)
Видим, что прогнали всего 2 теста. В т.ч. в браузере Chrome — 1, и он прошел успешно, в браузере Safari — также 1 и также успешно. Все замечательно.
А что там было насчет code coverage?
CodeCoverage подключается отдельным плагином. Данные о покрытии могут либо отображаться в виде статической информации (файл такой-то покрыт на N%) по окончании исполнения тестов, либо могут выгружаться в файл формата LCOV. Авторы предлагают генерировать визуальные отчеты с помощью тулзы genhtml. Беглый поиск портированных под Win32 результатов не дал, поднимать Cygwin или отдельную машину для построения отчетов не хочется...
Запустим тесты с code coverage
Подключим плагин. Отредактируем конфигурационный файл (conf).
load:
- code.js
- test.js
server: http://localhost:4224
plugin:
- name: "coverage"
jar: "plugins/coverage.jar"
module: "com.google.jstestdriver.coverage.CoverageModule"
Запустим тесты
H:\jstd>java -jar JsTestDriver.jar --config conf --tests all
Safari: Runner reset.
.Safari: Runner reset.
Chrome: Runner reset.
.Chrome: Runner reset.
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (1,00 ms)
Chrome 6.0.472.63 Windows: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (1,00 ms)
Safari 525.28.1 Windows: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (0,00 ms)
H:/jstd/code.js: 83.33333% covered
H:/jstd/test.js: 100.0% covered
Видим, что дополнительно к результатам появилась информация о покрытии кода. От такого отчета толку чуть более чем никакого. Начнем сохранять результаты тестов в файл.
H:\jstd>java -jar JsTestDriver.jar --config conf --tests all --testOutput ./out
Safari: Runner reset.
.Safari: Runner reset.
Chrome: Runner reset.
.Chrome: Runner reset.
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (1,00 ms)
Chrome 6.0.472.63 Windows: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (1,00 ms)
Safari 525.28.1 Windows: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (1,00 ms)
Теперь информации о покрытии не видно совсем, но в папке ./out появился файл с покрытием в формате LCOV.
Формат LCOV
Формат lcov-файла, генерируемого js-test-driver предельно прост.
SF:H:/jstd/code.js
DA:1,2
DA:2,2
DA:5,2
DA:6,2
DA:7,0
DA:9,2
end_of_record
SF:H:/jstd/test.js
DA:1,2
DA:3,2
DA:4,2
DA:5,2
end_of_record
SF — файл, для которого приводятся данне далее, DA — данные о покрытии (DA: Строка, СколькоРазВыполнена).
Генерируем красивый отчет: PHP_CodeCoverage
PHPUnit — фреймворк дле реализации юнит-тестирования для PHP, имеет возможность генерировать отчеты о code coverage. Модуль, занимающийся CodeCoverage, очень легко отделяем и очень аккуратно реализован. В состав входит интерфейс PHP_CodeCoverage_Driver, классы, имплементирующие его, могут служить источником данных о Code coverage для прочих компонентов проекта (построитель отчетов в т.ч.).
Xdebug. Как он отдает данные о покрытии?
Для файла...
<?php
xdebug_start_code_coverage();
function a() {
$x = 10;
}
$b = 30;
var_dump(xdebug_get_code_coverage());
Получим результат...
array(
'Z:\home\test\www\test.php' =>
array(
4 => 1
8 => 1
10 => 1
)
);
Видно что форматы очень похожи, можно сделать свой драйвер
Ниже — простой пример кода, генерирующего отчет о покрытии. Предполагается, что данные о покрытии находятся в файле coverage.dat. Отчет будет расположен в папке CodeCoverageReport.
<?php
include('PHP/CodeCoverage.php');
include('PHP/CodeCoverage/Driver/Lcov.php');
include('PHP/CodeCoverage/Report/HTML.php');
// ./lcov_coverage.dat contains ine coverage report in LCOV format
$coverage = new PHP_CodeCoverage(new PHP_CodeCoverage_Driver_Lcov('./coverage.dat'));
$coverage->start('mytest');
$coverage->stop();
$writer = new PHP_CodeCoverage_Report_HTML();
$writer->process($coverage, 'CodeCoverageReport');
Что получается, можно посмотреть здесь. Можно посмотреть на отчет и увидеть, что наш сложный пример не полностью покрыт тестами, пропущена одна ветка и ее надо срочно покрыть тестами.