Статьи

Тестовое покрытие кода: от мифа к реальности

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

Но времена изменились. Большинство рабочих мест в кабинках исчезло, и программисты начали любить свое ремесло. С появлением Agile-технологий и движением Software Craftsmanship появилось много новых инструментов, помогающих программисту и процессу. TDD постепенно становится де-факто способом написания кода, и секреты SCRUM или Kanban были открыты даже программистам в самых темных уголках мира.

Автоматизированное тестирование и разработка через тестирование (TDD) являются одними из основных методов, которые Agile предоставляет нам, программистам. И инструмент, который поставляется с этими методологиями, используется для создания тестового кода, который является темой этой статьи.

«В информатике охват кода — это мера, используемая для описания степени, в которой исходный код программы тестируется определенным набором тестов». ~ Википедия

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

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

Мы будем использовать PHP в качестве языка для иллюстрации нашего кода. Кроме того, нам понадобятся PHPUnit и XDebug для тестирования нашего кода и сбора данных покрытия.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
class WordWrap {
 
    public function wrap($string = », $cols) {
        $string = trim($string);
        if (strlen($string) > $cols) {
            $lastSpaceIndex = strrpos(substr($string, 0, $cols), ‘ ‘);
            if ($lastSpaceIndex !== false && substr($string, $cols, 1) != ‘ ‘) {
                return substr($string, 0, $lastSpaceIndex) .
            } else {
                return substr($string, 0, $cols) .
            }
        }
 
        return $string;
    }
}

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

Мы написали этот код с помощью Test Driven Development (TDD), и у нас есть 100% покрытие кода. Это означает, что, выполняя наш тест, мы проверяем каждую строку исходного кода.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
require_once __DIR__ .
 
class WordWrapTest extends PHPUnit_Framework_TestCase {
 
    function testItCanWrap() {
        $w = new WordWrap();
 
        $this->assertEquals(», $w->wrap(null, 0));
        $this->assertEquals(», $w->wrap(», 0));
        $this->assertEquals(‘a’, $w->wrap(‘a’, 1));
        $this->assertEquals(«a\nb», $w->wrap(‘a b’, 1));
        $this->assertEquals(«ab\nc», $w->wrap(‘ab c’, 3));
        $this->assertEquals(«a\nbc\nd», $w->wrap(‘a bc d’, 3));
    }
}

Одним из способов получения данных покрытия является запуск наших тестов в CLI (интерфейс командной строки) и анализ выходных данных. В этом примере мы будем использовать UNIX-подобную операционную систему (Linux, MacOS, FreeBSD и т. Д.). Пользователям Windows нужно будет немного адаптировать пути и имена исполняемых файлов, но это должно быть довольно похоже.

Давайте откроем консоль и изменим каталоги в вашей test папке. Затем запустите phpunit с возможностью генерировать данные покрытия в виде простого текста.

1
phpunit —coverage-text=./coverage.txt ./WordWrapTest.php

Это должно работать «из коробки» на большинстве систем, если установлен XDebug, однако в некоторых случаях вы можете столкнуться с ошибкой, связанной с часовыми поясами.

1
2
3
4
5
6
PHP Warning: date(): It is not safe to rely on the system’s timezone settings.
You are *required* to use the date.timezone setting or the date_default_timezone_set() function.
In case you used any of those methods and you are still getting this warning, you most likely
misspelled the timezone identifier.
date.timezone to select your timezone.
PHP_CodeCoverage-1.2.10/PHP/CodeCoverage/Report/Text.php on line 124

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

1
date.timezone = Europe/Bucharest

Теперь, если вы снова запустите команду phpunit , вы не увидите сообщений об ошибках. Вместо этого будут показаны результаты теста.

1
2
3
4
PHPUnit 3.7.20 by Sebastian Bergmann.
..
Time: 0 seconds, Memory: 5.00Mb
OK (2 tests, 7 assertions)

И данные покрытия будут в указанном текстовом файле.

01
02
03
04
05
06
07
08
09
10
11
12
$ cat ./coverage.txt
 
Code Coverage Report
  2014-03-02 13:48:11
 
 Summary:
  Classes: 100.00% (1/1)
  Methods: 100.00% (1/1)
  Lines: 2.68% (14/522)
 
WordWrap
  Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 7/ 7)

Давайте немного разберемся с этим.

  • Классы : относится к тому, сколько классов было протестировано и сколько из них было охвачено. WordWrap — наш единственный класс.
  • Методы : так же, как с классами. У нас есть только наш метод wrap() , больше ничего.
  • Строки : те же, что и выше, но для строк кода. Здесь у нас много строк, потому что сводка содержит все строки из самого PHPUnit.
  • Тогда у нас есть раздел для каждого класса. В нашем случае это только WordWrap . Каждый раздел имеет свои собственные методы и детали строки.

Основываясь на этих наблюдениях, мы можем сделать вывод, что наш код на 100% покрыт тестами. Точно так, как мы ожидали, прежде чем анализировать данные покрытия.

Просто изменив простой параметр для PHPUnit, мы можем генерировать хороший вывод HTML.

1
2
$ mkdir ./coverage
$ phpunit —coverage-html ./coverage ./WordWrapTest.php

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

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

Предыдущие примеры были интересны и весьма полезны, если ваш код построен на каком-то удаленном сервере, к которому у вас есть только SHH или веб-доступ. Но разве не было бы неплохо иметь всю эту информацию, жить в вашей IDE?

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

Информация о покрытии будет присутствовать в вашей IDE несколькими способами и в нескольких местах:

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

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

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

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

1
2
3
4
5
function testItCanWrap() {
    $w = new WordWrap();
    $this->assertEquals(«ab\nc», $w->wrap(‘ab c’, 3));
    $this->assertEquals(«a\nbc\nd», $w->wrap(‘a bc d’, 3));
}

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

Покрытие кода — это индикатор состояния, а не единица измерения производительности или правильности.

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

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

Ниже приведено покрытие кода для приложения конфигурации Syneto StorageOS.

Всего около 35%, но результаты требуют интерпретации. Большинство модулей находятся в зеленом, с охватом более 70%. Однако есть одна папка, Vmware , которая опускает среднюю. Это модуль с большим количеством классов, содержащий только определения для коммуникационного API. Нет причин проверять эти классы. Они были автоматически сгенерированы доверенным кодом. Программисты будут знать это, и они будут знать, как интерпретировать результаты. Менеджер может настаивать на его тестировании, потому что это красная полоса, и это выглядит подозрительно для тех, кто не знает внутренних деталей проекта. Есть ли смысл проверять это? Не за что! Это был бы бесполезный тест, который занимал бы драгоценные десятки секунд времени сборки без какого-либо преимущества.

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