Использование инструментов покрытия кода поможет выявить, где это необходимо

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

Завершение не обязательно должно означать 100%.

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

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

Осведомленность - это начало

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

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

Благодаря покрытию в реальном времени вы также будете знать, как только вы нарушите существующий код, покрытый тестами. Не только перед запуском в CI, но и во время ввода, перед тем, как вы отметите или даже сохраните исходный файл, над которым вы работаете!

Почему и когда важна осведомленность?

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

Большинство разработчиков не понимают, что каждый оператор if / else, switch case, enum, break и continue или ранний возврат - только разработчик (помимо рецензента кода) знает о них, и не все из них будут отображать непосредственно к требованию, на которое QA может ссылаться при создании своего пакета. И сколько из них вы напишете за неделю, месяц или год? Умножьте это на количество разработчиков в вашем магазине, и вы сможете быстро представить себе количество непроверенного кода, которое у вас есть.

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

Вещи, которые нельзя закрывать

Начиная с очевидного, определенно есть кусочки клея, шаблон, хуки приложений на уровне ОС и код инициализации головного проекта, которые не стоит подвергать тестированию. И, конечно же, другие основы, которым вы можете доверять, чтобы они «просто работали». А затем у вас есть сервисы и код пользовательского интерфейса, который вы можете просто заглушить или смоделировать. Также есть код, который лучше всего подходит для интеграционных тестов или автоматизации пользовательского интерфейса, поэтому покрытие модульными тестами определенно не всегда применимо.

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

Выбор не закрывать что-либо (надеюсь, по уважительной причине) всегда намного лучше, чем просто не думать о прикрытии своего кода.

Пункты для рассмотрения

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

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

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

Нетривиальное преобразование данных. Есть примитивы маршалинга, простые DTO или пакеты данных, которые делают именно то, что вы ожидаете в 99,9% случаев, но есть тема преобразования более сложных типов данных. Что, если преобразование имеет форматирование или другую мутацию, неявно примененную к выходным данным? Что делать, если вы используете нестандартные или «умные» методы де / сериализации?

Явное определение ожидаемых результатов

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

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

Где покрытие не удается

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

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

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

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

Последние мысли

Есть так много веских причин любить инструменты покрытия кода, и, надеюсь, теперь меньше причин не любить после прочтения этого. Независимо от того, хотите ли вы обеспечить покрытие кода на 80% или на 30%, это действительно зависит от вас, и нет однозначного «правильного» или «неправильного». Идея состоит в том, чтобы знать, где вы стоите, и осознавать пробелы, и в идеале это предварительное решение, а не запоздалая мысль.

Все любят хороший поворот сюжета

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

Это потому, что, хотя модульные тесты и покрытие кода отлично подходят для алгоритмов, взаимодействий подсистем и общего состояния кодовой базы, в конечном итоге важно то, что программное обеспечение работает так, как ожидается для пользователя или заинтересованной стороны; то, что находится под капотом, к ним не относится (но должно быть для разработчика). И лучший способ определить и протестировать с точки зрения принятия пользователями - это разработка, основанная на поведении.

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

Вывод

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

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

Как статья? Люблю это, ненавижу, я что-то пропустил? Я хочу услышать ваше мнение в разделе комментариев.