Отладка приложений

       

Блочное тестирование


Я всегда думал, что Энди Гроув (Andy Grove) из Intel имел право назвать свою книгу Only the Paranoid Survive (Выживают только параноики). Это особенно справедливо для инженеров-программистов. Объединяя чьи-нибудь программные модули со своими, проверяйте все чужие данные до последнего бита. На самом деле надо испытывать здоровый скептицизм даже по отношению к самому себе. Утверждения, трассировка и комментарии — вот с чего следует начинать проверку коллег-разработчиков, когда их программы обращаются к вашему модулю. Блочное тестирование — это средство самопроверки, с помощью которого я сам проверяю себя.

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

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

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

Дополнительной выгодой от тестирования проекта является то, что появляется возможность быстро находить и решать проблемы, что, в свою очередь, делает код повторно-используемым и расширяемым. Поскольку возможность многократного использования — священна для программирования, то любые усилия, потраченные на это, оправданы. Мне удалось удачно решить эту проблему при разработке обработчика аварийных сбоев, описанного в главе 9. Тестируя блоки в Windows 98, я заметил, что API-функция syminitiaiize из символьной машины DBGHELP.DLL перестала автоматически загружать символы всех модулей в процессах, как она это делала в Windows 2000. Предположив, что для решения этой задачи в Windows 98 нужна совсем другая утилита, я разработал функцию Bsusyminitialize. С ее помощью я выполнил все блочное тестирование программы обработчика аварийных сбоев, завершив тем самым эту разработку универсальным решением проблемы многократного использования.

Во время кодирования следует постоянно выполнять блочные тесты. По моим представлениям, в отдельном функциональном блоке содержится приблизительно 50 строк кода. Каждый раз, добавляя или изменяя свойство, я повторно запускаю блочный тест, чтобы видеть, не испортил ли я что-нибудь. Неожиданности следует сводить к минимуму. Настоятельно рекомендую выполнять блочные тесты до регистрации кода в главных источниках (master sources). Некоторые организации используют специальные тесты, называемые регистрационными тестами, которые нужно выполнять перед регистрацией кода. Такие тесты значительно уменьшают число аварий при построениях программ и smoke-тестах.

Ключом к большинству эффективных блочных тестов является понятие покрытия (охвата) кода (code coverage).


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

Статистику покрытия кода можно получить двумя способами. Первый способ довольно трудоемок. Он предполагает использование отладчика и установку точек прерывания на каждом исполняемом операторе тестируемого модуля. Когда во время отладочного прогона модуль выполнит очередной оператор, очистите его точку прерывания. Продолжайте выполнение программы до тех пор, пока не будут очищены все точки прерываний (в этом случае покрытие кода считается 100%). Легче подсчитывать процент покрытия при помощи специальных программных инструментов независимых поставщиков, например, TrueCoverage от NuMega или Visual PureCoverage фирмы Rational.

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

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

Следуя рекомендациям этой главы, можно получить в конце разработки довольно эффективные блочные тесты, но работа на этом не заканчивается. Мои блочные тесты можно найти в каталоге ..\SourceCode\BugslayerUtil \Tests на сопровождающем компакт-диске. Я храню их как часть свой кодовой базы, так что коллеги могут легко их отыскать. Когда я вношу изменения в эту базу, то могу легко проверить, не нарушил ли я что-нибудь. Настоятельно рекомендую всем разработчикам установить свои тесты в систему управления версией. Хотя большинство блочных тестов самодокументировано, удостоверьтесь, что вы комментируете все ключевые предположения так, чтобы другие могли быстрее ознакомиться с вашими тестами.



Содержание раздела