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

Что значит иметь 100% тестовое покрытие?

Отлично, у вас 100% тестовое покрытие, но что это на самом деле означает? 100% покрытие тестами просто означает, что вы написали достаточное количество тестов, чтобы покрыть каждую строку кода в вашем приложении. Вот и все, ни больше, ни меньше. Если вы правильно структурировали свои тесты, это теоретически означает, что вы можете предсказать, что какой-то вход сделает, чтобы получить какой-то результат. Теоретически… Это не значит, что вы на самом деле написали тест, чтобы убедиться, что ожидаемый результат действительно возвращается. Это может просто означать, что вы написали тест для другой части приложения, и в процессе была выполнена строка.

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

Почему стоит получить 100% тестовое покрытие

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

Вы можете найти неиспользуемый код

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

Вы можете найти сломанный код

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

Вы не так легко сломаете свой код при рефакторинге

Итак, представьте, что вы достигли 100% охвата после исправления всех ошибок и удаления всего неиспользуемого кода, убедившись, что покрыли большинство, если не все сценарии, с которыми может иметь дело ваш код, довольно приятное чувство, верно? Что ж, теперь наступает одно из моих любимых преимуществ стопроцентного покрытия тестами: рефакторинг старого кода и написание новых функций. Когда у вас есть тесты, вы теоретически знаете, какой результат вы получите для определенного ввода. Это очень мощная концепция, поскольку она означает, что вы можете реорганизовать свой код любым способом, который только можете вообразить, и получить мгновенную обратную связь по рефакторингу. Вы мгновенно узнаете, сломали ли вы код или все еще работает. Тесты ожидают определенного результата для данного ввода, и как только он изменится, он сообщит вам об этом. Конечно, некоторые модульные тесты могут дать сбой, потому что они основаны на конкретных реализациях вашего кода, но интеграционные тесты не сломаются, поскольку им все равно, как вы решите проблему, главное, чтобы она давала ожидаемый результат.

Вы можете доверять своему процессу

И последнее преимущество, о котором я могу думать, — это ощущение безопасности и уверенности в надежности кода. Доверие к системе зависит от уровня доверия, которое вы вкладываете в тесты. Так что, если вы пишете тесты, но не очень доверяете результату, возможно, пришло время написать больше и/или более качественные тесты. Вы должны иметь возможность полагаться на свои тесты, так как они представляют собой правильное функционирование вашего приложения для внешнего мира. Если вы доверяете результатам тестов, вы сможете выпускать новые функции намного быстрее. Если вы не доверяете результатам своих тестов, вы не будете писать тесты в долгосрочной перспективе, поскольку они являются препятствием для получения того, что вам нужно: «работающей» системы. Я намеренно написал это в скобках, потому что нет возможности проверить, действительно ли написанный вами код работает. Конечно, в первый раз можно протестировать вручную, но через 10 фич уже не сделаешь. Тогда, если какие-то новые функции сломают этот скрипт, вы не узнаете, пока кто-нибудь не обратит на это ваше внимание.

Почему достижение 100% покрытия тестами может быть пустой тратой времени

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

Отчеты о покрытии легко обмануть

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

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

100% охват не означает, что все работает как надо

Как я упоминал в последнем абзаце, то, что вы выполнили каждую строку кода, не означает, что вы на самом деле убедились, что он работает должным образом. Это означает, что на виду может скрываться любое количество неожиданных ошибок. Например, вы написали тесты для контроллера и убедились, что все методы контроллера работают должным образом. Это отличное начало, но вы еще не там. Что, если промежуточное ПО заблокирует доступ пользователя к контроллеру? Ну, вы не тестировали этот аспект. Таким образом, ваши тесты могут возвращать зеленый цвет, но ваше приложение не работает должным образом. Это базовый пример, но вы поняли мою мысль: несколько способов ведут к определенному результату, и вам нужно убедиться, что все эти способы работают должным образом.

Тесты могут вводить в заблуждение

Тесты могут вводить в заблуждение. Это особенно верно, если вы пишете тесты после того, как уже написали код. Вы можете найти метод, который не был протестирован должным образом, поэтому вы пишете для него тест. Отличное начало! Но что произойдет, если вы утвердите, что возвращается определенный результат, хотя этот результат изначально является ошибкой? Пример: ваш вход равен 1, а ожидаемый результат равен 3. Итак, вы пишете тест, который проверяет, что 1 вернет 3, и это так. Зеленый, код работает. Да вроде так, но если метод возвращает 1+1, то ваше утверждение изначально неверно. Это очень простой пример, но стоит остановиться и подумать, что он означает. Это означает, что вы написали тест, который гарантирует, что вы никогда не найдете ошибку автоматически, пока клиент не наткнется на нее. Процесс написания тестов заключается в том, чтобы убедиться, что вы понимаете свой код и формируете его по своему желанию. Тесты — ваш основной источник правды, поэтому убедитесь, что вы можете положиться на результаты своих тестов.

Нет серебряной пули

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

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

Так стоит ли стопроцентное покрытие тестами? Ну, это зависит от ситуации. Часто это имеет много преимуществ, но опять же, это не лучшее решение для написания отличного программного обеспечения.

Что вы думаете о 100% тестовом покрытии? Вы когда-нибудь активно отказывались от 100% охвата? Почему? Спасибо, что дочитали до этого места. Дайте мне знать в Твиттере, что вы думаете об этой теме.

Опубликовано: 6 ноября 2019 г.

Первоначально опубликовано на https://roelofjanelsinga.com.