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

Что такое покрытие кода? Обычно это число, которое говорит вам, какая часть вашего кода «покрыта» тестами. Чтобы вычислить это число, тестовые фреймворки просто подсчитывают количество строк кода, которые были выполнены при запуске набора тестов. Потом просто дают цифру, например, 60%.

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

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

Но давайте подумаем, как мы интерпретируем показатели покрытия кода? Если набор тестов пройден, все тесты зеленые — значит наша логика верна, верно? Если эти тесты покрывают 100% кода приложения, значит наше приложение на 100% корректно, не так ли? Это именно та ловушка, в которую легко попасть. Следуя этой логике, можем ли мы сказать, что приложение с 80-процентным покрытием кода на 50 % правильнее (или содержит меньше ошибок), чем приложение с всего лишь 40-процентным покрытием кода? Эдсгер В. Дейкстра утверждал, что Тестирование программы может быть использовано для выявления наличия ошибок, но никогда не для демонстрации их отсутствия. Следуя его логике, наш набор тестов покажет, что в проверенной нами логике 0 ошибок. Интуитивно кажется расширить эту логику, чтобы сказать, что это означает отсутствие ошибок. Но! Это показывает нам, что ошибок 0, в ситуациях, которые мы тестировали. Он ничего не говорит о тех ситуациях, которые мы не тестировали в нашем наборе.

«Но подождите!» — скажете вы, у нас покрытие кода показывает 100%, значит, мы все протестировали. Но фреймворки для тестирования довольно тупые, покрытие кода просто подсчитывает количество строк, которые вы протестировали, а не все логически возможные ситуации. Они не будут учитываться, если вы протестировали все возможные входные данные. Абсолютно возможно иметь 100% охват, запускать каждую строку кода, но не проверять все логические ситуации.

Рассмотрим эту примитивную функцию в качестве примера (независимо от того, на каком языке):

double divide(double a, double b) {
  return a/b;
}

и у нас есть хороший тест для этого:

void test_divide(){
  assertEquals(2, divide(2/1));
}

О покрытии кода будем рады сообщить 100%! Но что произойдет, если b равно 0? Все тесты зеленые и покрытие кода 100%, но проблемы в коде все равно есть!

Итак, если стопроцентное покрытие кода не является хорошей целью, то что? Стоит ли тратить время очень дорогого инженера на написание тестов для достижения определенного показателя покрытия кода? Можем ли мы сказать, что 40% или 60% достаточно?

Достижение 100% (или любого очень высокого процента) — очень дорогое и потенциально расточительное предприятие, только подумайте обо всех тех геттерах/сеттерах, которые присутствуют в современных языках ООП и их примитивной логике!

Возможно, покрытие кода на самом деле не имеет значения? Качество тестов важнее их количества. Если, например, мы возьмем принцип Парето в качестве руководства, мы можем подумать, что покрытие кода всего 20% должно быть в состоянии обнаружить 80% проблем. Пока тесты хорошие, их количество не имеет большого значения.

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