Статьи

Покрытие кода как инструмент рефакторинга

Использование покрытия кода для рефакторинга в сочетании с TDD является мощным инструментом. В этой статье обсуждается как.

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

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

Но охват кода должен быть менее актуален при использовании Test Driven Development, не так ли? TDD автоматически дает 100% покрытие, верно? Когда вы используете дисциплинированный подход TDD, должен быть провальный тест, оправдывающий каждую строку написанного кода. И наоборот, каждая строка кода, которую вы пишете, направлена ​​на провал теста. Поэтому должно быть невозможно иметь покрытие кода менее 100%, если вы правильно используете TDD. Ниже этого означает, что вы не выполняете TDD должным образом.

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

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

Давайте посмотрим на несколько примеров.

Непроверенные классы

На скриншоте, показанном здесь, класс имеет 0% охват. Если вы разрабатываете с использованием TDD, все ваши тесты, как правило, будут тестироваться на некотором этапе, и большинство (если не все) будут тестироваться непосредственно с помощью модульных тестов. Класс с 0% тестовым охватом в этом контексте действительно странный.

Есть несколько возможных объяснений. Ваш класс может быть эффективно проверен интеграционным или функциональным тестом. В качестве альтернативы, иногда класс в одном модуле Maven эффективно тестируется модулем через класс в другом модуле: в этом случае покрытие может не быть получено инструментом тестирования покрытия (например, Cobertura не будет обнаруживать покрытие в этом случае). В обоих случаях это нормально, если это работает для вас, или если вы не можете поступить иначе, но, возможно, ваши тесты должны быть ближе к классам, которые они тестируют?

Тем не менее, довольно часто, 0% охват класса указывает на класс, который больше нигде не используется. Это именно тот случай, поэтому класс можно безопасно удалить.

Непроверенные методы

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

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

Непроверенные строки или условия

Пропущенные строки или условия в методе также могут иногда поднимать красные флажки, особенно если тестовое покрытие ранее было выше. Не полностью проверенные условия охраны являются особенно распространенной формой этого. На следующем снимке экрана Cobertura показывает, что проверка на нулевое значение никогда не выполняется полностью — другими словами, параметр description никогда не является нулевым. Существует множество причин, по которым эта проверка могла быть размещена там (небрежная отладка является обычной), но в любом случае, если мы практикуем TDD с какой-либо строгостью, это условие больше не требуется.

На самом деле, как показано в твиттере от «Дяди» Боба Мартина ниже, условие защиты, такое как проверка на ноль, особенно в непубличном методе, является флагом, который говорит: «Я не знаю, как этот метод используется используемый».

Например, рассмотрим следующий код:

private doStuffTo(Client client) {
if (client != null) {
// do stuff
}
}

 

Так почему же мы тестируем на ноль? Должно ли это быть написано так?

private doStuffTo(Client client) {
if (client != null) {
// do stuff
} else {
throw new WTFException();
}
}

 

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

import static com.google.common.base.Preconditions.checkNotNull;

private doStuffTo(Client client) {
checkNotNull(client)
// do stuff
}

 

Но, конечно, еще лучше понять ваш код — почему в этот метод в первую очередь передается значение null? Разве это не признак ошибки в коде вызова? Там, где это возможно, я бы предпочел что-то подобное:

private doStuffTo(Client client) {
// just do stuff
}

 

 

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

 

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

Джон является известным международным консультантом, инструктором и докладчиком в области разработки и тестирования с открытым исходным кодом и гибкой Java-разработки. Он специализируется на оказании помощи командам разработчиков в улучшении их игр с помощью таких методов, как разработка через тестирование (включая BDD и ATDD), автоматизированные приемочные тесты, непрерывная интеграция, автоматизация сборки и методы чистого кодирования. В ближайшие месяцы он будет проводить онлайн-семинары по разработке через тестирование и автоматизированному веб-тестированию для европейской аудитории с 31 мая по 3 июня, а также полный трехдневный семинар по TDD / BDD / ATDD в Сиднее (20-22 июня). ) и Веллингтон (дата будет объявлена ​​позднее), и говорить наНикакого пуха, просто вещи ÜberConf в Денвере в июле.