Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 812
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
ГЛАВА 22 Тестирование, выполняемое разработчиками
511
работке требований или проектировании. Возможно, исправить отдельную ошиб- ку конструирования и дешевле, но при учете всех ошибок ситуация меняется.
Ниже приведены некоторые мои выводы.
쐽
В небольших проектах дефекты конструирования составляют боль- шинство всех ошибок. В одном исследовании ошибок кодирования, до- пущенных в небольшом проекте (1000 строк кода), было обнаружено, что
75% дефектов пришлось на этап кодирования, тогда как на этап выработки тре- бований — 10%, а на этап проектирования — 15% (Jones, 1986a). По-видимому,
такое распределение ошибок характерно для многих небольших проектов.
쐽
Дефекты конструирования составляют минимум 35% всех дефектов независи- мо от размера проекта. В крупных проектах доля дефектов конструирования не так велика, как в небольших, но и тогда она равна минимум 35% (Beizer, 1990;
Jones, 2000). Некоторые ученые сообщают, что даже в очень крупных проек- тах дефектами конструирования являются около 75% всех ошибок (Grady, 1987).
Обычно чем лучше разработчики знают прикладную область, тем лучше они проектируют общую архитектуру системы. В этом случае на детальное проек- тирование и кодирование приходится еще больший процент ошибок (Basili and
Perricone, 1984).
쐽
Хотя ошибки конструирования и дешевле исправлять, чем ошибки выработки требований и проектирования, это все равно дорого. Исследование двух очень крупных проектов, проведенное в Hewlett-Packard, показало, что стоимость исправления среднего дефекта конструирования составляла 25–50% от стоимо- сти исправления средней ошибки проектирования (Grady, 1987). При учете большего числа дефектов конструирования общая стоимость их исправления могла вдвое превышать стоимость исправления дефектов проектирования.
Вот примерное отношение между объемом проекта и распределением ошибок
(рис. 22-2):
Рис. 22-2. По мере увеличения объема проекта доля ошибок, допускаемых
во время конструирования, снижается. Тем не менее ошибки конструирования
составляют 45–75% всех ошибок даже в самых крупных проектах
512
ЧАСТЬ V Усовершенствование кода
Сколько ошибок можно ожидать?
Ожидаемое число ошибок зависит от качества вашего процесса разработки. Вот некоторые соображения по этому поводу.
쐽
Средний для отрасли показатель таков: примерно 1–25 ошибок на 1000
строк кода в готовом ПО, разрабатывавшегося с использованием разных методик (Boehm, 1981; Gremillion, 1984; Yourdon, 1989a; Jones, 1998; Jones,
2000; Weber, 2003). Проекты, в которых обнаруживается 0,1 от указанного числа ошибок, редки; о случаях, когда ошибок в 10 раз больше, предпочитают не сообщать (вероятно, такие проекты даже не доводят до конца!).
쐽
Отделение прикладного ПО компании Microsoft сообщает о таких показате- лях, как 10–20 дефектов на 1000 строк кода во время внутреннего тестирова- ния и 0,5 дефекта на 1000 строк кода в готовой продукции (Moore, 1992). Для достижения этого уровня применяется комбинация методик чтения кода, опи- санных в разделе 21.4, и независимого тестирования.
쐽
Харлан Миллз придумал «разработку методом чистой комнаты» — методику,
позволяющую достигнуть всего лишь 3 дефектов на 1000 строк кода во время внутреннего тестирования и 0,1 дефекта на 1000 строк кода в готовой системе
(Cobb and Mills, 1990). В нескольких проектах — например, в проекте разработ- ки ПО для космических кораблей — благодаря сочетанию методик формальной разработки, обзоров кода коллегами и статистического тестирования был до- стигнут такой уровень, как 0 дефектов на 500 000 строк кода (Fishman, 1996).
쐽
Уоттс Хамфри (Watts Humphrey) сообщает, что группы, применяющие мето- дику Team Software Process (TSP), достигают уровня 0,06 дефекта на 1000 строк кода. Главный аспект TSP — обучение разработчиков изначальному предот- вращению дефектов (Weber, 2003).
Результаты проектов TSP и проектов «чистой комнаты» подтверждают другую версию Главного Закона Контроля Качества ПО: дешевле сразу создать высококачественную программу, чем создать низкокачественную программу и исправлять ее. Производительность труда в случае одного полнос- тью проверенного проекта из 80 000 строк, разработанного методом «чистой комнаты», оказалась равной 740 строкам кода на человеко-месяц. В то же время средняя для отрасли скорость разработки полностью проверенного кода, учиты- вающая все затраты, не связанные с кодированием, близка к 250–300 строкам на человеко-месяц (Cusumano et al., 2003). Экономия затрат и повышение произво- дительности труда при использовании методик TSP или «чистой комнаты» объяс- няются тем, что эти методики почти исключают затраты времени на отладку. Что?
Исключают затраты времени на отладку? Это действительно стоящая цель!
Ошибки самого тестирования
Вероятно, вам знакома следующая ситуация. В программе имеется ошибка.
У вас сразу же возникают некоторые предположения о ее причинах, но весь код кажется правильным. Пытаясь изолировать ошибку, вы выпол- няете еще несколько тестов, но все они дают правильные результаты. Вы прово- дите несколько часов за чтением кода и пересчетом результатов вручную, но без- результатно. Еще через несколько часов что-то заставляет вас проверить тесто-
ГЛАВА 22 Тестирование, выполняемое разработчиками
513
вые данные. Эврика! Ошибка в тестовых данных! Как глупо тратить часы на по- иск ошибки, если она кроется в тестовых данных, а не в коде!
Это довольно распространенная ситуация. Зачастую сами тесты содер- жат не меньше, а то и больше ошибок, чем тестируемый код (Weiland, 1983;
Jones, 1986a; Johnson, 1994). Объяснить это легко, особенно если разра- ботчики сами пишут тесты. Тесты обычно создают на ходу, не уделяя должного внимания проектированию и конструированию. Их часто считают одноразовы- ми и разрабатывают с соответствующей тщательностью.
Ниже дано несколько советов по снижению числа ошибок в тестах.
Проверяйте свою работу Разрабатывайте тесты так же тщательно, как и код,
и внимательно проверяйте их. Анализируйте код каждого теста строка за стро- кой при помощи отладчика, как вы проверяли бы код готового приложения. Про- водите анализ и инспекцию тестовых данных.
Планируйте тестирование программы так же, как и ее разработку
Планирование тестирования следует начинать на этапе выработки требований или сразу после получения задания. Это поможет избавиться от тестов, основанных на неверных предположениях.
Храните тесты Посвятите немного времени проверке качества тестов. Сохра- ните их для регрессивного тестирования и для работы над будущими версиями программы. Связанные с этим проблемы легко оправдать, если вы знаете, что собираетесь сохранить тесты, а не выбросить.
Встраивайте блочные тесты в среду тестирования Пишите сначала код блочных тестов, но затем интегрируйте их в системную среду тестирования (та- кую как JUnit). Использование интегрированной среды тестирования предотвра- щает только что упомянутую тенденцию выбрасывать тесты.
22.5. Инструменты тестирования
В этом разделе рассматриваются типы инструментов тестирования, которые вы можете купить или создать. Конкретные инструменты названы здесь не будут,
потому что ко времени издания книги они вполне могут устареть. Для получения свежих сведений обратитесь к своему любимому журналу по программированию.
Создание лесов для тестирования отдельных классов
Термин «леса» (scaffolding) пришел в программирование из строительства. Стро- ительные леса создаются для того, чтобы рабочие получили доступ к определен- ным частям здания. В программировании леса создаются исключительно для упрощения тестирования кода.
Одним из типов лесов является объект, используемый дру- гим, тестируемым объектом. Такой объект называется «под- дельным объектом» (mock object) или «объектом-заглушкой»
(stub object) (Mackinnon, Freemand, and Craig, 2000; Thomas and Hunt, 2002). С аналогичной целью можно создавать и низкоуровневые методы, называемые «заглушками». Поддель- ные объекты или методы-заглушки можно делать более или
Дополнительные сведения Не- сколько хороших примеров ле- сов можно найти в эссе Джона
Бентли «A Small Matter of Prog- ramming» в книге «Programming
Pearls, 2d ed.» (Bentley, 2000).
514
ЧАСТЬ V Усовершенствование кода менее реалистичными в зависимости от требуемой степени достоверности. Такие леса могут:
쐽
немедленно возвращать управление, не выполнив никаких действий;
쐽
тестировать полученные данные;
쐽
выводить диагностическое сообщение (например, значения входных парамет- ров) на экран или записывать в файл;
쐽
запрашивать возвращаемые значения у программиста;
쐽
возвращать стандартный ответ независимо от полученных данных;
쐽
впустую тратить ресурсы процессора, выделенные реальному объекту или методу;
쐽
играть роль более медленной, объемной, простой или менее точной версии реального объекта или метода.
Другим типом лесов является поддельный метод, вызывающий реальный тести- руемый метод. Такой метод, называемый «драйвером» или иногда «тестовой сбруей»
(test harness), может:
쐽
вызывать объект, передавая ему некоторые данные;
쐽
запрашивать информацию у программиста и передавать ее объекту;
쐽
принимать параметры командной строки (если ОС это поддерживает) и пе- редавать их объекту;
쐽
читать аргументы из файла и передавать их объекту;
쐽
вызывать объект несколько раз, передавая ему при каждом вызове новый пред- определенный набор входных данных.
Последний тип лесов, фиктивный файл (упрощенная вер- сия реального полноразмерного файла), включает такие же элементы. Небольшой фиктивный файл обладает двумя до- стоинствами. Его небольшой объем позволяет держать в уме все его содержимое и облегчает проверку правильности фай- ла. А так как файл создается специально для тестирования,
вы можете спроектировать его так, чтобы любая ошибка в его использовании бро- салась в глаза.
Очевидно, что создание лесов требует некоторой работы, но вы сможете повторно использовать их даже при обнаружении ошибки в классе. Кроме того, существуют многие инструмен- ты, упрощающие создание поддельных объектов и других видов лесов. При тести- ровании класса леса позволяют исключить его взаимодействие с другими классами.
Леса особенно полезны, если в программе применяются утонченные алгоритмы. Так,
если тестируемый код встроен в другой блок кода, тесты могут выполняться непри- емлемо медленно. Леса позволяют тестировать код непосредственно. Несколько минут,
потраченных на создание лесов для тестирования кода, скрытого в самых глубинах программы, могут сэкономить много часов на отладке.
Для создания лесов годится любая из многих доступных сред тестирования (JUnit,
CppUnit, NUnit и т. д.). Если ваша среда разработки не поддерживается ни одной из существующих сред тестирования, вы можете написать несколько методов класса и включить в файл метод
main() для их тестирования, пусть даже эти методы не
Перекрестная ссылка Грань между инструментами тестиро- вания и инструментами отлад- ки размыта. Об инструментах отладки см. раздел 23.5.
http://cc2e.com/2268
ГЛАВА 22 Тестирование, выполняемое разработчиками
515
будут в итоговой программе работать сами по себе. Метод
main() может читать параметры командной строки и передавать их в тестируемый метод, позволяя проверить его до интеграции с остальной частью программы. Выполняя инте- грацию, оставьте методы и предназначенные для их тестирования леса в файле,
и заблокируйте леса при помощи директив препроцессора или комментариев. В
итоге код лесов будет проигнорирован при компиляции, а если вы разместите его в конце файла, он и на глаза не будет попадаться. Оставленный в файле, он не причинит никакого вреда. Вам не придется тратить время на его удаление и ар- хивирование, а в случае чего он всегда будет под рукой.
Инструменты сравнения файлов
Регрессивное (или повторное) тестирование значительно облегчают автоматизированные инструменты сравнения действительных выходных данных с ожидаемыми. Данные,
выводимые на печать, легко проверить, перенаправив их в файл и сравнив при помощи diff или другой утилиты срав- нения файлов с другим файлом, в который ранее были записаны ожидаемые дан- ные. Если файлы различаются, радуйтесь: вы обнаружили регрессивную ошибку.
Генераторы тестовых данных
Вы также можете написать код для систематичного тести- рования выбранных фрагментов программы. Несколько лет назад я разработал собственный алгоритм шифрования и написал на его основе программу шифрования файлов. Программа должна была так кодировать файл, чтобы его можно было декодировать, только введя правиль- ный пароль. При шифровании содержимое файла изменялось самым радикаль- ным образом. Было очень важно, чтобы программа декодировала файл правиль- но, потому что в противном случае он был бы испорчен.
Я настроил генератор тестовых данных, который полностью тестировал шифро- вальную и дешифровальную части программы. Он генерировал файлы, состоящие из случайных символов и имеющие случайный объем в пределах от 0 до 500 кб. Он генерировал случайные пароли случайной длины в диапазоне от 1 до 255 симво- лов. Для каждого случая он генерировал две копии случайного файла, шифровал одну копию, заново инициализировался, дешифровал копию и затем сравнивал дешифрованную копию с первоначальной. Если какие-нибудь байты различались,
генератор печатал всю информацию, нужную мне для воспроизведения ошибки.
Я настроил генератор так, чтобы средний объем тестируемых файлов равнялся
30 кб. Если бы я этого не сделал, файлы были бы равномерно распределены меж- ду 0 кб и 500 кб и имели средний объем 250 кб. Сокращение среднего объема позволило мне быстрее тестировать файлы, пароли, признаки конца файла, не- обычные объемы файлов и прочие возможные причины ошибок.
Результаты не заставили себя ждать. Выполнив лишь около 100 тестов, я нашел две ошибки. Обе возникали в специфических ситуациях, которые могли ни разу не случиться на практике, однако это все же были ошибки, и я был рад их обна- ружить. После их исправления я тестировал программу несколько недель, зашиф-
Перекрестная ссылка О регрес- сивном тестировании см. подраз- дел «Повторное (регрессивное)
тестирование» раздела 22.6.
http://cc2e.com/2275
516
ЧАСТЬ V Усовершенствование кода ровав и дешифровав около 100 000 файлов без каких бы то ни было ошибок.
Учитывая то, что я протестировал файлы и пароли самого разного объема и со- держания, я мог быть уверен, что с программой все в порядке.
Из этой истории можно извлечь ряд уроков, в том числе следующие.
쐽
Правильно спроектированные генераторы случайных данных способны гене- рировать комбинации тестовых данных, о которых вы могли бы не подумать.
쐽
Генераторы случайных данных могут протестировать программу тщательнее,
чем вы сами.
쐽
Со временем вы можете улучшить случайные тесты, обратив повышенное вни- мание на реалистичный диапазон входных значений. Это позволяет сконцен- трировать тестирование на тех областях, которые будут использоваться чаще всего, и максимально повысить их надежность.
쐽
Модульное проектирование сполна окупается во время тестирования. Я смог отделить код шифрования и дешифрования и задействовать его независимо от кода пользовательского интерфейса, что упростило задачу написания тес- тового драйвера.
쐽
Вы можете повторно использовать тестовый драйвер даже после изменения тестируемого им кода. Как только я исправил две ошибки, я смог сразу же на- чать повторное тестирование.
Мониторы покрытия кода тестами
Карл Вигерс сообщает, что тестирование, выполняемое без измерения покрытия кода тестами, обычно охватывает только 50–60% кода (Wiegers,
2002). Монитор покрытия — это инструмент, который следит за тем, какой код тестировался, а какой нет. Монитор покрытия особенно полезен для систе- матичного тестирования, потому что он говорит вам, полностью ли код покры- вается конкретным набором тестов. Если вы выполнили полный набор тестов, а монитор покрытия сообщает, что какой-то код все еще не протестирован, значит,
нужны дополнительные тесты.
Регистраторы данных
Некоторые инструменты могут следить за программой и собирать информацию о ее состоянии в случае краха по- добно «черному ящику», устанавливаемому в самолетах для определения причин крушения. Эффективная регистрация данных облегчает ди- агностику ошибок и сопровождение программы после ее выпуска.
Вы можете создать собственный регистратор данных, записывающий сведения о важных событиях в файл. Записывайте в файл состояние системы до ошибки и подробные сведения об условиях возникновения ошибки. Можете использовать эту функциональность в предварительных версиях программ и блокировать в окончательных версиях. Если вы используете для хранения зарегистрированных данных самоочищающееся хранилище и тщательно продумали размещение и содержание сообщений об ошибках, можете оставлять функции регистрации дан- ных и в итоговых версиях программ.
http://cc2e.com/2282
511
работке требований или проектировании. Возможно, исправить отдельную ошиб- ку конструирования и дешевле, но при учете всех ошибок ситуация меняется.
Ниже приведены некоторые мои выводы.
쐽
В небольших проектах дефекты конструирования составляют боль- шинство всех ошибок. В одном исследовании ошибок кодирования, до- пущенных в небольшом проекте (1000 строк кода), было обнаружено, что
75% дефектов пришлось на этап кодирования, тогда как на этап выработки тре- бований — 10%, а на этап проектирования — 15% (Jones, 1986a). По-видимому,
такое распределение ошибок характерно для многих небольших проектов.
쐽
Дефекты конструирования составляют минимум 35% всех дефектов независи- мо от размера проекта. В крупных проектах доля дефектов конструирования не так велика, как в небольших, но и тогда она равна минимум 35% (Beizer, 1990;
Jones, 2000). Некоторые ученые сообщают, что даже в очень крупных проек- тах дефектами конструирования являются около 75% всех ошибок (Grady, 1987).
Обычно чем лучше разработчики знают прикладную область, тем лучше они проектируют общую архитектуру системы. В этом случае на детальное проек- тирование и кодирование приходится еще больший процент ошибок (Basili and
Perricone, 1984).
쐽
Хотя ошибки конструирования и дешевле исправлять, чем ошибки выработки требований и проектирования, это все равно дорого. Исследование двух очень крупных проектов, проведенное в Hewlett-Packard, показало, что стоимость исправления среднего дефекта конструирования составляла 25–50% от стоимо- сти исправления средней ошибки проектирования (Grady, 1987). При учете большего числа дефектов конструирования общая стоимость их исправления могла вдвое превышать стоимость исправления дефектов проектирования.
Вот примерное отношение между объемом проекта и распределением ошибок
(рис. 22-2):
Рис. 22-2. По мере увеличения объема проекта доля ошибок, допускаемых
во время конструирования, снижается. Тем не менее ошибки конструирования
составляют 45–75% всех ошибок даже в самых крупных проектах
512
ЧАСТЬ V Усовершенствование кода
Сколько ошибок можно ожидать?
Ожидаемое число ошибок зависит от качества вашего процесса разработки. Вот некоторые соображения по этому поводу.
쐽
Средний для отрасли показатель таков: примерно 1–25 ошибок на 1000
строк кода в готовом ПО, разрабатывавшегося с использованием разных методик (Boehm, 1981; Gremillion, 1984; Yourdon, 1989a; Jones, 1998; Jones,
2000; Weber, 2003). Проекты, в которых обнаруживается 0,1 от указанного числа ошибок, редки; о случаях, когда ошибок в 10 раз больше, предпочитают не сообщать (вероятно, такие проекты даже не доводят до конца!).
쐽
Отделение прикладного ПО компании Microsoft сообщает о таких показате- лях, как 10–20 дефектов на 1000 строк кода во время внутреннего тестирова- ния и 0,5 дефекта на 1000 строк кода в готовой продукции (Moore, 1992). Для достижения этого уровня применяется комбинация методик чтения кода, опи- санных в разделе 21.4, и независимого тестирования.
쐽
Харлан Миллз придумал «разработку методом чистой комнаты» — методику,
позволяющую достигнуть всего лишь 3 дефектов на 1000 строк кода во время внутреннего тестирования и 0,1 дефекта на 1000 строк кода в готовой системе
(Cobb and Mills, 1990). В нескольких проектах — например, в проекте разработ- ки ПО для космических кораблей — благодаря сочетанию методик формальной разработки, обзоров кода коллегами и статистического тестирования был до- стигнут такой уровень, как 0 дефектов на 500 000 строк кода (Fishman, 1996).
쐽
Уоттс Хамфри (Watts Humphrey) сообщает, что группы, применяющие мето- дику Team Software Process (TSP), достигают уровня 0,06 дефекта на 1000 строк кода. Главный аспект TSP — обучение разработчиков изначальному предот- вращению дефектов (Weber, 2003).
Результаты проектов TSP и проектов «чистой комнаты» подтверждают другую версию Главного Закона Контроля Качества ПО: дешевле сразу создать высококачественную программу, чем создать низкокачественную программу и исправлять ее. Производительность труда в случае одного полнос- тью проверенного проекта из 80 000 строк, разработанного методом «чистой комнаты», оказалась равной 740 строкам кода на человеко-месяц. В то же время средняя для отрасли скорость разработки полностью проверенного кода, учиты- вающая все затраты, не связанные с кодированием, близка к 250–300 строкам на человеко-месяц (Cusumano et al., 2003). Экономия затрат и повышение произво- дительности труда при использовании методик TSP или «чистой комнаты» объяс- няются тем, что эти методики почти исключают затраты времени на отладку. Что?
Исключают затраты времени на отладку? Это действительно стоящая цель!
Ошибки самого тестирования
Вероятно, вам знакома следующая ситуация. В программе имеется ошибка.
У вас сразу же возникают некоторые предположения о ее причинах, но весь код кажется правильным. Пытаясь изолировать ошибку, вы выпол- няете еще несколько тестов, но все они дают правильные результаты. Вы прово- дите несколько часов за чтением кода и пересчетом результатов вручную, но без- результатно. Еще через несколько часов что-то заставляет вас проверить тесто-
ГЛАВА 22 Тестирование, выполняемое разработчиками
513
вые данные. Эврика! Ошибка в тестовых данных! Как глупо тратить часы на по- иск ошибки, если она кроется в тестовых данных, а не в коде!
Это довольно распространенная ситуация. Зачастую сами тесты содер- жат не меньше, а то и больше ошибок, чем тестируемый код (Weiland, 1983;
Jones, 1986a; Johnson, 1994). Объяснить это легко, особенно если разра- ботчики сами пишут тесты. Тесты обычно создают на ходу, не уделяя должного внимания проектированию и конструированию. Их часто считают одноразовы- ми и разрабатывают с соответствующей тщательностью.
Ниже дано несколько советов по снижению числа ошибок в тестах.
Проверяйте свою работу Разрабатывайте тесты так же тщательно, как и код,
и внимательно проверяйте их. Анализируйте код каждого теста строка за стро- кой при помощи отладчика, как вы проверяли бы код готового приложения. Про- водите анализ и инспекцию тестовых данных.
Планируйте тестирование программы так же, как и ее разработку
Планирование тестирования следует начинать на этапе выработки требований или сразу после получения задания. Это поможет избавиться от тестов, основанных на неверных предположениях.
Храните тесты Посвятите немного времени проверке качества тестов. Сохра- ните их для регрессивного тестирования и для работы над будущими версиями программы. Связанные с этим проблемы легко оправдать, если вы знаете, что собираетесь сохранить тесты, а не выбросить.
Встраивайте блочные тесты в среду тестирования Пишите сначала код блочных тестов, но затем интегрируйте их в системную среду тестирования (та- кую как JUnit). Использование интегрированной среды тестирования предотвра- щает только что упомянутую тенденцию выбрасывать тесты.
22.5. Инструменты тестирования
В этом разделе рассматриваются типы инструментов тестирования, которые вы можете купить или создать. Конкретные инструменты названы здесь не будут,
потому что ко времени издания книги они вполне могут устареть. Для получения свежих сведений обратитесь к своему любимому журналу по программированию.
Создание лесов для тестирования отдельных классов
Термин «леса» (scaffolding) пришел в программирование из строительства. Стро- ительные леса создаются для того, чтобы рабочие получили доступ к определен- ным частям здания. В программировании леса создаются исключительно для упрощения тестирования кода.
Одним из типов лесов является объект, используемый дру- гим, тестируемым объектом. Такой объект называется «под- дельным объектом» (mock object) или «объектом-заглушкой»
(stub object) (Mackinnon, Freemand, and Craig, 2000; Thomas and Hunt, 2002). С аналогичной целью можно создавать и низкоуровневые методы, называемые «заглушками». Поддель- ные объекты или методы-заглушки можно делать более или
Дополнительные сведения Не- сколько хороших примеров ле- сов можно найти в эссе Джона
Бентли «A Small Matter of Prog- ramming» в книге «Programming
Pearls, 2d ed.» (Bentley, 2000).
514
ЧАСТЬ V Усовершенствование кода менее реалистичными в зависимости от требуемой степени достоверности. Такие леса могут:
쐽
немедленно возвращать управление, не выполнив никаких действий;
쐽
тестировать полученные данные;
쐽
выводить диагностическое сообщение (например, значения входных парамет- ров) на экран или записывать в файл;
쐽
запрашивать возвращаемые значения у программиста;
쐽
возвращать стандартный ответ независимо от полученных данных;
쐽
впустую тратить ресурсы процессора, выделенные реальному объекту или методу;
쐽
играть роль более медленной, объемной, простой или менее точной версии реального объекта или метода.
Другим типом лесов является поддельный метод, вызывающий реальный тести- руемый метод. Такой метод, называемый «драйвером» или иногда «тестовой сбруей»
(test harness), может:
쐽
вызывать объект, передавая ему некоторые данные;
쐽
запрашивать информацию у программиста и передавать ее объекту;
쐽
принимать параметры командной строки (если ОС это поддерживает) и пе- редавать их объекту;
쐽
читать аргументы из файла и передавать их объекту;
쐽
вызывать объект несколько раз, передавая ему при каждом вызове новый пред- определенный набор входных данных.
Последний тип лесов, фиктивный файл (упрощенная вер- сия реального полноразмерного файла), включает такие же элементы. Небольшой фиктивный файл обладает двумя до- стоинствами. Его небольшой объем позволяет держать в уме все его содержимое и облегчает проверку правильности фай- ла. А так как файл создается специально для тестирования,
вы можете спроектировать его так, чтобы любая ошибка в его использовании бро- салась в глаза.
Очевидно, что создание лесов требует некоторой работы, но вы сможете повторно использовать их даже при обнаружении ошибки в классе. Кроме того, существуют многие инструмен- ты, упрощающие создание поддельных объектов и других видов лесов. При тести- ровании класса леса позволяют исключить его взаимодействие с другими классами.
Леса особенно полезны, если в программе применяются утонченные алгоритмы. Так,
если тестируемый код встроен в другой блок кода, тесты могут выполняться непри- емлемо медленно. Леса позволяют тестировать код непосредственно. Несколько минут,
потраченных на создание лесов для тестирования кода, скрытого в самых глубинах программы, могут сэкономить много часов на отладке.
Для создания лесов годится любая из многих доступных сред тестирования (JUnit,
CppUnit, NUnit и т. д.). Если ваша среда разработки не поддерживается ни одной из существующих сред тестирования, вы можете написать несколько методов класса и включить в файл метод
main() для их тестирования, пусть даже эти методы не
Перекрестная ссылка Грань между инструментами тестиро- вания и инструментами отлад- ки размыта. Об инструментах отладки см. раздел 23.5.
http://cc2e.com/2268
ГЛАВА 22 Тестирование, выполняемое разработчиками
515
будут в итоговой программе работать сами по себе. Метод
main() может читать параметры командной строки и передавать их в тестируемый метод, позволяя проверить его до интеграции с остальной частью программы. Выполняя инте- грацию, оставьте методы и предназначенные для их тестирования леса в файле,
и заблокируйте леса при помощи директив препроцессора или комментариев. В
итоге код лесов будет проигнорирован при компиляции, а если вы разместите его в конце файла, он и на глаза не будет попадаться. Оставленный в файле, он не причинит никакого вреда. Вам не придется тратить время на его удаление и ар- хивирование, а в случае чего он всегда будет под рукой.
Инструменты сравнения файлов
Регрессивное (или повторное) тестирование значительно облегчают автоматизированные инструменты сравнения действительных выходных данных с ожидаемыми. Данные,
выводимые на печать, легко проверить, перенаправив их в файл и сравнив при помощи diff или другой утилиты срав- нения файлов с другим файлом, в который ранее были записаны ожидаемые дан- ные. Если файлы различаются, радуйтесь: вы обнаружили регрессивную ошибку.
Генераторы тестовых данных
Вы также можете написать код для систематичного тести- рования выбранных фрагментов программы. Несколько лет назад я разработал собственный алгоритм шифрования и написал на его основе программу шифрования файлов. Программа должна была так кодировать файл, чтобы его можно было декодировать, только введя правиль- ный пароль. При шифровании содержимое файла изменялось самым радикаль- ным образом. Было очень важно, чтобы программа декодировала файл правиль- но, потому что в противном случае он был бы испорчен.
Я настроил генератор тестовых данных, который полностью тестировал шифро- вальную и дешифровальную части программы. Он генерировал файлы, состоящие из случайных символов и имеющие случайный объем в пределах от 0 до 500 кб. Он генерировал случайные пароли случайной длины в диапазоне от 1 до 255 симво- лов. Для каждого случая он генерировал две копии случайного файла, шифровал одну копию, заново инициализировался, дешифровал копию и затем сравнивал дешифрованную копию с первоначальной. Если какие-нибудь байты различались,
генератор печатал всю информацию, нужную мне для воспроизведения ошибки.
Я настроил генератор так, чтобы средний объем тестируемых файлов равнялся
30 кб. Если бы я этого не сделал, файлы были бы равномерно распределены меж- ду 0 кб и 500 кб и имели средний объем 250 кб. Сокращение среднего объема позволило мне быстрее тестировать файлы, пароли, признаки конца файла, не- обычные объемы файлов и прочие возможные причины ошибок.
Результаты не заставили себя ждать. Выполнив лишь около 100 тестов, я нашел две ошибки. Обе возникали в специфических ситуациях, которые могли ни разу не случиться на практике, однако это все же были ошибки, и я был рад их обна- ружить. После их исправления я тестировал программу несколько недель, зашиф-
Перекрестная ссылка О регрес- сивном тестировании см. подраз- дел «Повторное (регрессивное)
тестирование» раздела 22.6.
http://cc2e.com/2275
516
ЧАСТЬ V Усовершенствование кода ровав и дешифровав около 100 000 файлов без каких бы то ни было ошибок.
Учитывая то, что я протестировал файлы и пароли самого разного объема и со- держания, я мог быть уверен, что с программой все в порядке.
Из этой истории можно извлечь ряд уроков, в том числе следующие.
쐽
Правильно спроектированные генераторы случайных данных способны гене- рировать комбинации тестовых данных, о которых вы могли бы не подумать.
쐽
Генераторы случайных данных могут протестировать программу тщательнее,
чем вы сами.
쐽
Со временем вы можете улучшить случайные тесты, обратив повышенное вни- мание на реалистичный диапазон входных значений. Это позволяет сконцен- трировать тестирование на тех областях, которые будут использоваться чаще всего, и максимально повысить их надежность.
쐽
Модульное проектирование сполна окупается во время тестирования. Я смог отделить код шифрования и дешифрования и задействовать его независимо от кода пользовательского интерфейса, что упростило задачу написания тес- тового драйвера.
쐽
Вы можете повторно использовать тестовый драйвер даже после изменения тестируемого им кода. Как только я исправил две ошибки, я смог сразу же на- чать повторное тестирование.
Мониторы покрытия кода тестами
Карл Вигерс сообщает, что тестирование, выполняемое без измерения покрытия кода тестами, обычно охватывает только 50–60% кода (Wiegers,
2002). Монитор покрытия — это инструмент, который следит за тем, какой код тестировался, а какой нет. Монитор покрытия особенно полезен для систе- матичного тестирования, потому что он говорит вам, полностью ли код покры- вается конкретным набором тестов. Если вы выполнили полный набор тестов, а монитор покрытия сообщает, что какой-то код все еще не протестирован, значит,
нужны дополнительные тесты.
Регистраторы данных
Некоторые инструменты могут следить за программой и собирать информацию о ее состоянии в случае краха по- добно «черному ящику», устанавливаемому в самолетах для определения причин крушения. Эффективная регистрация данных облегчает ди- агностику ошибок и сопровождение программы после ее выпуска.
Вы можете создать собственный регистратор данных, записывающий сведения о важных событиях в файл. Записывайте в файл состояние системы до ошибки и подробные сведения об условиях возникновения ошибки. Можете использовать эту функциональность в предварительных версиях программ и блокировать в окончательных версиях. Если вы используете для хранения зарегистрированных данных самоочищающееся хранилище и тщательно продумали размещение и содержание сообщений об ошибках, можете оставлять функции регистрации дан- ных и в итоговых версиях программ.
http://cc2e.com/2282