Pull to refresh

Юнит-тестирование и CodeCoverage для Javascript-кода

Reading time 4 min
Views 8K
В этой заметке расскажу о своем опыте юнит-тестирования JS-кода, опыте использования среды выполнения тестов js-test-driver, ее возможности code coverage и скручивании ежа с ужом, а именно данных о code coverage от js-test-driver и генератора отчетов о покрытии PHP_CodeCоverage. Расскажу и покажу как получить вот такие отчеты о покрытии кода...

Итак, потребовалось реализовать юнит-тестирование для 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');

Что получается, можно посмотреть здесь. Можно посмотреть на отчет и увидеть, что наш сложный пример не полностью покрыт тестами, пропущена одна ветка и ее надо срочно покрыть тестами.
Tags:
Hubs:
+61
Comments 29
Comments Comments 29

Articles