Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по
Скачать 5.88 Mb.
|
ГЛАВА 22 Тестирование, выполняемое разработчиками 517 Символические отладчики Символический отладчик — это технологическое дополне# ние анализа и инспекций кода. Отладчик может выполнять код строка за строкой, позволяет следить за значениями переменных и всегда интерпретирует код так же, как ком# пьютер. Возможность пошагового выполнения кода и наблю# дения за его работой трудно переоценить. Анализ кода при помощи отладчика во многом напоминает процесс обзора ва# шего кода другими программистами. Ни ваши коллеги, ни отладчик не имеют тех же слабых сторон, которые имеете вы. Дополнительное преимущество отладчика в том, что анализ кода с его помощью менее трудоемок, чем групповой обзор. Наблюдение за выполнением кода при разных комбинациях входных данных позволяет убедиться в том, что вы написали то, что хотели. Кроме того, использование хорошего отладчика является эффективным способом изучения языка, поскольку вы можете получить точную информацию о выполне# нии кода. Переключаясь между высокоуровневым языком и ассемблером, вы мо# жете изучить соответствие высокоуровневых и низкоуровневых конструкций. Наблюдение за регистрами и стеком позволяет лучше разобраться в передаче аргументов в методы. Вы можете увидеть, как компилятор оптимизирует код. Ни# какое из этих преимуществ не имеет прямого отношения к главной роли отлад# чика — диагностике уже обнаруженных ошибок, но при творческом подходе из отладчика можно извлечь гораздо большую выгоду. Инструменты возмущения состояния системы Другой класс инструментов тестирования предназначен для приведения системы в возмущенное состояние. Многие программисты могут рассказать истории о программах, ко# торые работают 99 раз из 100, но при сотом запуске с теми же данными терпят крах. Почти всегда причиной этого является невыполнение инициализации ка# кой#то переменной, и обычно эту ошибку очень трудно воспроизвести, потому что в 99 случаях из 100 неинициализированная переменная получает нулевое значение. Инструменты тестирования из этого класса могут иметь самые разнообразные возможности. Заполнение памяти Эта функция помогает находить неинициализирован# ные переменные. Некоторые инструменты перед запуском программы запол# няют память произвольными значениями, чтобы неинициализированные пе# ременные не получили случайно значение 0. Иногда память целесообразно за# полнять конкретным значением. Например, в случае процессоров с архитек# турой x86 значение 0xCC соответствует машинному коду команды прерыва# ния. Если вы заполните память значением 0xCC и программа попробует вы# полнить что#то, чего выполнять не следует, вы натолкнетесь в отладчике на точку прерывания и обнаружите ошибку. «Встряхивание» памяти В многозадачных средах некоторые инструменты могут переупорядочивать выделенную программе память, что позволяет гаран# Перекрестная ссылка Наличие отладчиков зависит от зрелос- ти технологической среды (см. раздел 4.3). http://cc2e.com/2289 518 ЧАСТЬ V Усовершенствование кода тировать отсутствие кода, требующего, чтобы данные находились по абсолют# ным, а не относительным адресам. Селективный сбой памяти Драйвер памяти позволяет имитировать недо# статок или неудачный запрос памяти, может отказывать в запросе памяти после произвольного числа успешных запросов или предоставлять запрошенную память после произвольного числа неудовлетворенных запросов. Это особен# но полезно при тестирования сложных программ, выделяющих память дина# мически. Проверка доступа к памяти (проверка границ) Инструменты проверки границ следят за операциями над указателями, гарантируя их корректность. Такие инструменты полезны при поиске неинициализированных или «вися# чих» указателей. Базы данных для хранения информации об ошибках БД, содержащая информацию об обнаруженных ошибках, — тоже мощный инструмент тестирования. Такую базу мож# но рассматривать и как инструмент управления, и как тех# нический инструмент. Она позволяет узнавать о появлении старых ошибок, сле# дить за частотой обнаружения и исправления новых ошибок, а также за статусом и тяжестью исправленных и неисправленных ошибок. О том, какую информацию об ошибках следует хранить в БД, вы узнаете в разделе 22.7. 22.6. Оптимизация процесса тестирования Способы улучшения тестирования аналогичны способам улучшения любого дру# гого процесса. Опираясь на точные знания о процессе, вы немного изменяете его и смотрите, к каким результатам это приводит. Если результат изменения поло# жителен, значит, процесс стал несколько лучше. В следующих подразделах опти# мизация процессов рассматривается в контексте тестирования. Планирование тестирования Эффективность тестирования заметно повышается, если его спланировать в самом начале работы над проектом. При# своение тестированию той же степени важности, что и про# ектированию с кодированием, подразумевает, что на тес# тирование будет выделено время, что оно будет считаться не менее важным и что процесс тестирования будет высо# кокачественным. Планирование тестирования нужно и для обеспечения повторяемости процесса тестирования. Если процесс нельзя повторить, его нельзя улучшить. Повторное (регрессивное) тестирование Допустим, вы тщательно протестировали систему и не обнаружили ошибок. Пред# положим далее, что вы изменяете какой#то фрагмент системы и хотите убедить# ся в том, что она по#прежнему успешно проходит все тесты, т. е. что изменение Перекрестная ссылка Одним из элементов планирования тести- рования является формализа- ция планов в письменной фор- ме. О документировании тести- рования см. работы, указанных в разделе «Дополнительные ресурсы» главы 32. http://cc2e.com/2296 ГЛАВА 22 Тестирование, выполняемое разработчиками 519 не привело к появлению новых дефектов. Тестирование, призванное гарантиро# вать, что программа не сделала шаг назад, или не «регрессировала», называется «регрессивным». Почти невозможно создать высококачественную программу, если вы не можете си# стематично проводить ее повторного тестирования после внесения изменений. Если после каждого изменения выполнять другие тесты, вы не сможете с уверенностью сказать, что в программу не были внесены новые дефекты. Так что при регрессив# ном тестировании нужно каждый раз выполнять одни и те же тесты. По мере разви# тия системы можно добавлять новые тесты, но сохранять при этом и старые. Автоматизированное тестирование Единственный практичный способ управления регрессивным тестиро# ванием — его автоматизация. Многократное выполнение одних и тех же тестов, приводящих к тем же результатам, вводит людей в транс. Они утрачивают концентрацию и начинают упускать ошибки, что сводит на нет эф# фективность регрессивного тестирования. Гуру тестирования Борис Бейзер сооб# щает, что уровень ошибок, допускаемых при тестировании вручную, сравним с уровнем ошибок в самом тестируемом коде. По его оценкам, при тестировании, проводимом вручную, должным образом выполняется лишь около половины всех тестов (Johnson, 1994). Преимущества автоматизированного тестирования таковы. При автоматизированном тестировании вероятность допустить ошибку ниже, чем при тестировании вручную. Автоматизировав тестирование, вы сможете легко адаптировать его к другим компонентам системы. Автоматизация тестирования позволяет выполнять тесты гораздо чаще. Авто# матизация тестирования — один из базовых элементов интенсивных методик тестирования, таких как ежедневная сборка программы, дымовое тестирова# ние и экстремальное программирование. Автоматизированное тестирование способствует максимально раннему обна# ружению проблем, что обычно минимизирует объем работы, нужной для ди# агностики и исправления проблемы. Способствуя более быстрому обнаружению дефектов, внесенных в код при его изменении, автоматизированное тестирование снижает вероятность того, что вам придется позднее заняться крупномасштабным исправлением кода. Автоматизированное тестирование особенно полезно в новых изменчивых технологических средах, поскольку оно позволяет быстрее узнавать об изменениях среды. Многие инструменты, используемые для автоматизации тестирования, позволяют создавать тестовые леса, генери# ровать входные и регистрировать выходные данные и сравнивать действитель# ные выходные данные с ожидаемыми. Для выполнения этих функций можно ис# пользовать инструменты, обсуждавшиеся в предыдущем разделе. Перекрестная ссылка О связи между зрелостью технологий и методами разработки см. раз- дел 4.3. 520 ЧАСТЬ V Усовершенствование кода 22.7. Протоколы тестирования Обеспечить повторяемость процесса тестирования недостаточно — вы должны оценивать и проект, чтобы можно было точно сказать, улучша# ется он в результате изменений или ухудшается. Вот некоторые катего# рии данных, которые можно собирать с целью оценки проекта: административное описание дефекта (дата обнаружения, сотрудник, сообщив# ший о дефекте, номер сборки программы, дата исправления); полное описание проблемы; действия, предпринятые для воспроизведения проблемы; предложенные способы решения проблемы; родственные дефекты; тяжесть проблемы (например, критическая проблема, «неприятная» или кос# метическая); источник дефекта: выработка требований, проектирование, кодирование или тестирование; вид дефекта кодирования: ошибка занижения или завышения на 1, ошибка при# сваивания, недопустимый индекс массива, неправильный вызов метода и т. д.; классы и методы, измененные при исправлении дефекта; число строк, затронутых дефектом; время, ушедшее на нахождение дефекта; время, ушедшее на исправление дефекта. Собирая эти данные, вы сможете подсчитывать некоторые показатели, позволя# ющие сделать вывод об изменении качества проекта: число дефектов в каждом классе; все числа целесообразно отсортировать в порядке от худшего класса к лучшему и, возможно, нормализовать по размеру класса; число дефектов в каждом методе, все числа целесообразно отсортировать в порядке от худшего метода к лучшему и, возможно, нормализовать по разме# ру метода; среднее время тестирования в расчете на один обнаруженный дефект; среднее число обнаруженных дефектов в расчете на один тест; среднее время программирования в расчете на один исправленный дефект; процент кода, покрытого тестами; число дефектов, относящихся к каждой категории тяжести. Личные протоколы тестирования Кроме протоколов тестирования уровня проекта, вы можете хранить и личные протоколы тестирования. Можете включать в них контрольные списки ошибок, которые вы допускаете чаще всего, и указывать время, затрачиваемое вами на написание кода, его тестирование и исправление ошибок. ГЛАВА 22 Тестирование, выполняемое разработчиками 521 Дополнительные ресурсы Федеральные законы об объективности информации застав# ляют меня признаться в том, что в нескольких других кни# гах тестирование рассматривается подробнее, чем в этой главе. Например, в них можно найти материалы о тестировании системы и тес# тировании методом «черного ящика», которых мы не касались. Кроме того, в этих книгах более глубоко освещаются вопросы тестирования, выполняемого разра# ботчиками. В них обсуждаются формальные подходы к тестированию (такие как создание причинно#следственных диаграмм), а также детали создания независи# мой организации, занимающейся тестированием. Тестирование Kaner, Cem, Jack Falk, and Hung Q. Nguyen. Testing Computer Software, 2d ed. New York, NY: John Wiley & Sons, 1999. Наверное, это лучшая книга по тестированию ПО. Приведенная в ней информация касается в первую очередь тестирования программ, которые будут использоваться большим числом людей (например, крупных Web# сайтов и приложений, продаваемых в магазинах), но полезна и в общем. Kaner, Cem, James Bach, and Bret Pettichord. Lessons Learned in Software Testing. New York, NY: John Wiley & Sons, 2002. Эта книга хорошо дополняет предыдущую. Она разделена на 11 глав, включающих 250 уроков, изученных самими авторами. Tamre, Louise. Introducing Software Testing. Boston, MA: Addison#Wesley, 2002. Эта несложная книга ориентирована на разработчиков, которым нужно понять тес# тирование. Несмотря на название («Введение в тестирование ПО»), некоторые из приведенных в книге сведений будут полезны и опытным тестировщикам. Whittaker, James A. How to Break Software: A Practical Guide to Testing. Boston, MA: Addison#Wesley, 2002. В этой книге описаны 23 вида атак, которые тестировщики могут использовать для нарушения работы ПО, и приведены примеры каждой атаки с применением популярных программных пакетов. Из#за оригинального подхо# да эту книгу можно рассматривать и как основной источник информации о тес# тировании, и как дополнение других книг. Whittaker, James A. «What Is Software Testing? And Why Is It So Hard?» IEEE Software, January 2000, pp. 70–79. В этой статье можно найти хорошее введение в вопросы тестирования ПО и объяснение некоторых проблем, связанных с эффективным тестированием. Myers, Glenford J. The Art of Software Testing. New York, NY: John Wiley, 1979. Эта клас# сическая книга по тестированию ПО издается до сих пор (хотя и стоит немало). Ее содержание довольно простое: тестирование, основанное на самооценке; пси# хология и экономика тестирования программ; анализ и обзоры программ; про# ектирование тестов; тестирование классов; тестирование более высокого поряд# ка; отладка; инструменты тестирования и другие методики. Книга лаконична (177 страниц) и легко читается. Приведенный в ее начале тест поможет вам начать думать, как тестировщик, и продемонстрирует все разнообразие способов нару# шения работы кода. http://cc2e.com/2203 522 ЧАСТЬ V Усовершенствование кода Тестовые леса Bentley, Jon. «A Small Matter of Programming» in Programming Pearls, 2d ed. Boston, MA: Addison#Wesley, 2000. В этом эссе приведены хорошие примеры тестовых лесов. Mackinnon, Tim, Steve Freeman, and Philip Craig. «Endo#Testing: Unit Testing with Mock Objects», eXtreme Programming and Flexible Processes Software Engineering % XP2000 Conference, 2000. Первая работа, посвященная использованию поддельных объек# тов при тестировании, выполняемом разработчиками. Thomas, Dave and Andy Hunt. «Mock Objects» IEEE Software, May/June 2002. Эта ста# тья представляет собой удобочитаемое введение в использование поддельных объектов. www.junit.org. Это сайт поддержки разработчиков, использу# ющих среду JUnit. Аналогичные сайты см. по адресам cpp% unit.sourceforge.net и nunit.sourceforge.net. Разработка с изначальными тестами Beck, Kent. Test%Driven Development: By Example. Boston, MA: Addison#Wesley, 2003. Бек описывает «разработку через тестирование» — подход, предусматривающий первоначальное создание тестов и последующее написание кода, удовлетворяю# щего тестам. Хотя местами Бек впадает в евангелический тон, его советы очень полезны, а сама книга лаконична и попадает точно в цель. Стоит отметить, что она включает серьезный пример, для которого приводится реальный код. Соответствующие стандарты IEEE Std 1008%1987 (R1993) — стандарт блочного тестирования ПО. IEEE Std 829%1998 — стандарт документирования тестов ПО. IEEE Std 730%2002 — стандарт планирования контроля качества ПО. Контрольный список: тесты Каждому ли требованию, относящемуся к классу или методу, соответствует отдельный тест? Каждому ли аспекту проектирования, относящемуся к классу или методу, соответствует отдельный тест? Каждая ли строка кода протестирована хотя бы одним тестом? Проверили ли вы это, подсчитав минимальное число тестов, нужное для выполнения каждой строки кода? Каждый ли путь «определение — использование» в потоке данных протес- тирован хотя бы одним тестом? Проверили ли вы код на наличие в потоке данных путей, которые обычно ошибочны, таких как «определение — определение», «определение — вы- ход» и «определение — уничтожение»? Использовали ли вы список частых ошибок для написания тестов, направ- ленных на обнаружение ошибок, которые часто встречались в прошлом? Протестировали ли вы все простые граничные условия: максимальные, минимальные и граничные условия с завышением или занижением на 1? http://cc2e.com/2217 http://cc2e.com/2210 ГЛАВА 22 Тестирование, выполняемое разработчиками 523 Протестировали ли вы сложные граничные условия, т. е. комбинации вход- ных данных, которые могут приводить к слишком малому или большому значению вычисляемой переменной? Тестируете ли вы код на предмет неверных видов данных, таких как отри- цательное число сотрудников в программе расчета заработной платы? Тестируете ли вы код с использованием типичных средних значений? Тестируете ли вы минимальные нормальные конфигурации? Тестируете ли вы максимальные нормальные конфигурации? Тестируете ли вы совместимость со старыми данными? Тестируете ли вы код, работающий со старым оборудованием и старыми версиями операци- онной системы, а также интерфейсы со старыми версиями других программ? Позволяют ли тесты легко проверить результаты вручную? Ключевые моменты Тестирование, выполняемое разработчиками, — один из важнейших элемен# тов полной стратегии тестирования. Независимое тестирование не менее важно, но оно не является предметом этой книги. Написание тестов до написания кода требует примерно того же времени и тех же усилий, что и создание тестов после кода, но сокращает циклы регистра# ции — отладки — исправления дефектов. Даже если учесть, что тестирование имеет массу разновидностей, его все рав# но следует считать лишь одним из элементов хорошей программы контроля качества. Высококачественные методики разработки, позволяющие свести к минимуму число дефектов в требованиях и проектах, играют не менее, а то и более важную роль. Методики совместной разработки характеризуются не меньшей эффективностью обнаружения ошибок, чем тестирование, к тому же они позволяют находить другие ошибки. Опираясь на базисное тестирование, анализ потоков данных, анализ гранич# ных условий, классы плохих и классы хороших данных, вы можете создать много тестов детерминированным образом. Методика угадывания ошибок укажет вам на некоторые дополнительные тесты. Обычно ошибки концентрируются в нескольких наиболее дефектных классах и методах. Найдите такой код, перепроектируйте его и перепишите. Для тестовых данных обычно характерна более высокая плотность ошибок, чем для тестируемого кода. Так как поиск ошибок в тестовых данных требует вре# мени, не приводя к какому бы то ни было улучшению кода, эти ошибки более досадны, чем ошибки программирования. Избегайте их, разрабатывая тесты столь же тщательно, что и код. Автоматизация тестирования полезна вообще и практически необходима в случае регрессивного тестирования. Чтобы процесс тестирования был максимально эффективным, сделайте его регулярным, выполняйте его оценку и используйте получаемую информацию для его улучшения. 524 ЧАСТЬ V Усовершенствование кода Г Л А В А 2 3 Отладка Содержание 23.1. Общие вопросы отладки 23.2. Поиск дефекта 23.3. Устранение дефекта 23.4. Психологические аспекты отладки 23.5. Инструменты отладки — очевидные и не очень Связанные темы Качество ПО: глава 20 Тестирование, выполняемое разработчиками: глава 22 Рефакторинг: глава 24 Отладка — это процесс определения и устранения причин ошибок. Этим она отличается от тестирования, направлен# ного на обнаружение ошибок. В некоторых проектах отладка занимает до 50% общего времени разработки. Многие про# граммисты считают отладку самым трудным аспектом про# граммирования. Но отладка не обязана быть таковой. Если вы будете следо# вать советам, приведенным в этой книге, вам придется отла# живать меньше ошибок. Большинство дефектов будет тривиальными недосмотра# ми и опечатками, которые вы сможете легко находить, читая исходный код или вы# полняя его в отладчике. Что до остальных, более сложных ошибок, то, прочитав эту главу, вы сможете сделать их отладку гораздо более легкой. 23.1. Общие вопросы отладки Покойная Грейс Хоппер (Grace Hopper), контр#адмирал ВМС США и один из ав# торов языка COBOL, утверждала, что слово «bug» появилось в программировании во времена первого крупного цифрового компьютера — Mark I (IEEE, 1992). Раз# бираясь с причинами неисправности компьютера, программисты нашли в нем крупного мотылька, замкнувшего какую#то цепь, и с тех пор вину за все компью# http://cc2e.com/2361 Отлаживать код вдвое сложнее, чем писать. Поэтому, если при написании программы вы ис- пользуете весь свой интеллект, вы по определению недостаточно умны, чтобы ее отладить. Брайан Керниган (Brian W. Kernighan) |