Эффективный анализ покрытия кода с помощью Travis-CI и Coveralls

В статье рассматриваются теоретические вопросы анализа покрытия кода тестами и практический подход к реализации с помощью системы CI Travis-CI и системы анализа покрытия кода тестами Coveralls.

Цели анализа покрытия кода тестами

Качество кода — краеугольный камень любого проекта. Некачественный код с течением времени требует все больше и больше усилий на его поддержку, а внедрение новых возможностей зачастую требует существенно больших трудозатрат, чем в случае качественного кода. Само собой, что критерии качества не абсолютны, а определяются и видоизменяются в течение жизненного цикла кода.

Существует достаточно много методов и инструментов, которые позволяют работать с качеством кода как с технической так и с процессной, административной стороны. Данные инструменты в руках опытных специалистов способны обеспечить соответствие высоким стандартам и гордость за код. В других случаях, они могут полностью парализовать работу команды или оценивать не реальные свойства кода, которые существенно влияют на его качество, а стимулировать разработчиков подгонять код под прохождение необходимых проверок.

Один из главных механизмов поддержания качества кода на высоком уровне — тесты кода, которые широко используются при промышленной разработке. Однако, наличие тестов не гарантирует того, что код качественный. Необходимо понимание того, каким образом использовать данный инструмент для реализация “правильных и полезных” тестов и как отслеживать метрики и результаты тестирования ПО. Для решения данных задач используются системы CI и анализ покрытия кода тестами.

Анализ покрытия кода тестами (code coverage) является одним из полезных технических инструментов, который позволяет оценить то, насколько тесты проверяют части кода. Более того, он предоставляет дополнительные возможности, которые могут быть полезны для разных проектных ролей — разработчиков, инженеров качества, ревьюеров кода, проектного менеджера.

При внедрении в проект механизма проверки покрытия кода тестами, система производит анализ того, какие части кода фигурируют в тестах, а какие нет, что позволяет получить ряд важных данных:

  1. общий процент покрытия по всему проекту (интегральная характеристика, которая может быть полезна управляющему персоналу);
  2. общий процент покрытия класса, файла, метода (интегральная характеристика, которая позволяет отслеживать динамику качества покрытия по различным частям проекта);
  3. покрытые строки кода (неинтегральная характеристика, которая показывает существуют ли тесты, которые проверяют участки кода).

Как было отмечено ранее, анализ покрытия может быть использован различными участниками проекта, рассмотрим какую пользу могут извлечь те или иные проектные роли.

Самый эффективный способ начать использовать анализ покрытия — его внедрение на начальной стадии проекта, до создания кода. Это позволяет наиболее эффективно использовать данный инструмент для обеспечения качества в долгосрочной перспективе.

Разработчик

Разработчик реализует юнит, интеграционные и другие типы тестов для кода, который разрабатывает. В том случае, если разработчик использует анализ покрытия кода, он имеет возможность определять следующие проблемы с кодом:

  1. плохой дизайн кода — код с плохим дизайном сложно тестировать и сложно обеспечить его покрытие;

    к примеру, без необходимых абстракций для взаимодействия с внешними сервисами, код невозможно тестировать без специфической среды, соответственно разработчики будут стараться избегать реализовывать тесты для данного кода, что позволит определить проблемные части кода;

  2. мертвый код — с помощью анализа покрытия можно определить участки мертвого кода, которые не тестируются, как бы вы не пытались; этот код — кандидат на исключение;
  3. анализ покрытия поможет и в написании тестов; обнаружив, что покрытие низкое, вы можете реализовать специфические тесты, направленные на то, чтобы покрыть именно эти участки кода.

Таким образом, сопровождая код тестами и проводя анализ покрытия, можно обеспечить часть требований к коду как с точки зрения дизайна, так и в части изоляции, единой ответственности. С точки зрения разработчика, данные процессы чаще всего могут быть эффективно выполнены в рамках IDE, например, IntelliJ Idea поддерживает как проверку покрытия, так и подсветку непокрытых частей кода.

QA инженер

QA инженеры часто занимаются созданием автоматизированных интеграционных и имитационных тестов. Они могут использовать данные о покрытии для реализации тестов, которые разработчики не могут полноценно реализовать без использования специфических окружений. Данные окружения обязательно имеются в наличии у отдела QA, например, системы с тестовыми данными, песочницы. В этом случае отдел QA может создавать свой набор тестов и анализировать покрытие кода своим набором, таким образом суммарное покрытие может быть значительно выше, чем покрытие, которого достигают разработчики, работающие с Mock-объектами.

Кроме того, на отдел QA может быть возложена обязанность первичного ревью кода, в ходе которого проверяется покрытие кода тестами. В случае обнаружения непокрытых частей инженер QA может вернуть код на доработку.

Ревьюер

Ревьюер может более продуктивно определять сущности кода, реализованные без должного уровня абстракции от окружения, классы с плохим дизайном и нарушением принципа единой ответственности, плохо протестированный код без глубокого анализа, что может обеспечить экономию времени.

Проектный менеджер

Проектный менеджер отслеживает интегральные показатели покрытия кода тестами, определяя степень улучшения качества покрытия или его ухудшения, что позволяет учитывать объем работ на будущие итерации и своевременно идентифицировать ситуации, когда качество кода падает, что может негативно влиять на среднесрочные и долгосрочные результаты проектной команды.

Один из вариантов разработки с использованием анализа покрытия кода тестами приведен на следующем рисунке:

Изображенный процесс работы типичный для разработки продуктов среднего и крупного размера. В левой части процесса изображена работа над задачей по одной из моделей: разработка с unit-тестами или TDD, но в цикл включен дополнительный этап промежуточной оценки результата с помощью покрытия. Данный анализ может выполняться как с помощью встроенных в IDE инструментов, так и внешними средствами. Для ролей QA Engineer и Project Manager анализ покрытия также встроен в процесс.

Проблемы анализа покрытия

Анализ покрытия кода тестами — полезный и эффективный механизм. Однако, существует ряд факторов, которые мешают его применению в проектах. Некоторые из данных факторов перечислены ниже:

  1. сильная зависимость кода от окружения, отсутствие возможности эффективного управления тестовыми окружениями;
  2. отсутствие процесса непрерывной интеграции кода (Continuous integration);
  3. слабая дисциплина в части покрытия кода тестами;
  4. сжатые сроки проекта, когда тесты не пишутся вообще в ущерб среднесрочному и долгосрочному качеству продукта;
  5. низкая квалификация разработчиков.

Очевидно, вышеперечисленные причины — не все, которые определяют возможности использования анализа покрытия кода тестами, но вышеуказанные встречаются достаточно часто.

Если в проекте тестов нет, их мало, нет дисциплины их разработки, то инструмент не может использоваться. Типичный пример, разработчику (часто не самой высокой квалификации) ставится задание создать PoC продукта, при этом сроки предоставляются сжатые. Конечно, его первый позыв — написать что-то, что работает, при этом, поскольку часто работа выполняется в дни, максимум недели, ему удается держать все в голове. В процессе выполнения работы он не думает ни о качественном дизайне кода, ни о возможности абстрагирования от окружения.

Позже, этот PoC начинает развиваться до уровня продукта в рамках сложившегося дизайна, воспринимая текущую ситуацию как данность. В результате — даже писать тесты для кода весьма сложно (как минимум существенная часть кода остается не поддающейся тестированию).

Если, при этом, заказчик не понимает зачем необходим рефэкторинг, не готов тратить средства и время команды на то, что не дает никакого результата, то долгосрочные последствия для продукта могут быть крайне негативными.

Таким образом, самый эффективный способ начать использовать покрытие тестами — с самого начала проекта интегрировать в разработку среду CI, включая инструменты анализа покрытия, и утвердить процессы работы над кодом, которые бы включали понятные критерии соответствия кода в разрезе тестов и покрытия.

На данной стадии многие команды могут столкнуться со сложностью настройки экосистемы CI, которая бы эффективно решала как вопросы интеграции для брэнчей и PR, так и предоставляла удобный инструмент для анализа покрытия. Часто команды делают попытки добавить анализ покрытия кода в проекты, но без удобного инструмента анализ покрытия в лучшем случае используется эпизодически, а в худшем предоставляет только интегральные метрики по запросу заинтересованных лиц.

Иными словами, не будучи встроенным в цепочку работы над задачей, анализ покрытия не используется или настолько неудобен, что эффект от его использования сводится к минимуму. Существенный фактор для его использования — удобный инструмент визуального анализа, доступный и понятный для широкого круга заинтересованных лиц.

Далее мы рассмотрим инструменты, которые позволяют эффективно решить задачу интеграции системы CI и анализа покрытия кода в процесс разработки, при использовании репозитория на GitHub.

Непрерывная интеграция и анализ покрытия с Travis-CI и Coveralls

Travis-CI — известная система непрерывной интеграции, которая простым образом интегрируется с системой контроля версий GitHub. Не требует развертывания дополнительной инфраструктуры.

Coveralls — менее известная система анализа покрытия кода, которая интегрируется с Travis-CI и позволяет организовать удобный анализ покрытия без развертывания дополнительной инфраструктуры (хотя существует вариант для развертывания на площадке разработчика).

Данные системы позволяют бесплатно производить CI и анализ покрытия для открытых репозиториев GitHub, что удобно как для апробации, так и в случае работы над проектами с открытым кодом. В том случае, если используются закрытые репозитории кода, то необходимо приобретать платную подписку.

Интеграция Travis-CI, Coveralls с GitHub осуществляется как отображено на следующем рисунке:

Таким образом, для начала работы необходимо выполнить следующие шаги:

  1. авторизовать Travis-CI и Coveralls для доступа к вашему GitHub аккаунту;
  2. выбрать репозиторий, для которого активируется CI и анализ покрытия;
  3. произвести настройку опций обработки репозитория в интерфейсах Travis-CI, Coveralls (настройки по умолчанию часто достаточно);
  4. в репозитории создать файл .travis.yml, в котором указать директивы для запуска тестов, включая экспорт в Coveralls.

Для примера рассмотрим репозиторий одного из наших открытых проектов bwsw/imp, для которого настроена интеграция. Этот проект реализован на языке Scala, а для сборки используется SBT. Рассмотрим, какие изменения мы сделали в проекте для подключения Travis-CI и Coveralls.

Для других технологий разработки необходимо совершить похожие действия, специфичные для используемой платформы. За подробностями необходимо обратиться к документации Travis-CI и Coveralls.

Добавление в SBT плагина для анализа покрытия и Coveralls

В файл project/plugins.sbt добавили плагины:

addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.0")

addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.1.0")

Добавление зависимости scoverage-runtime в build.sbt

Данная зависимость необходима для запуска тестов и анализа покрытия из IntelliJ Idea.

libraryDependencies += "org.scoverage" % "scalac-scoverage-runtime_2.12" % "1.3.0"

Добавление файла спецификации тестирования для Travis-CI

В корне проекта создадим файл .travis.yml

language: scala
jdk: oraclejdk8
scala:
   - 2.12.2

script: "sbt clean coverage test"
after_success: "sbt coverageReport coveralls"

Обратите внимание на строки script и after_success, в которых происходит все самое интересное. SBT “coverage test” генерирует отчет покрытия тестов. При успешном завершении тестов (after_success) происходит загрузка результатов в систему Coveralls с помощью плагина coveralls. Таким образом, все фактические действия происходят на стороне Travis-CI, а в Coveralls загружается только лишь анализ покрытия.

Теперь на каждую загрузку кода в бранчи и PR-ы Travis-CI будет выполнять CI проекта, а результаты анализа покрытия тестами будут загружаться в Coveralls. В качестве примера того, как это выглядит для проекта IMP, откройте ссылку на страницу проекта в Coveralls.

Интерфейс Coveralls в удобной форме отображает как интегральные показатели, так и позволяет для каждого файла исходного кода увидеть покрытые и непокрытые части. Перемещение между сборками позволяет оценить динамику изменения показателей.