Файл: Лекции по программной инженерии.pdf

ВУЗ: Не указан

Категория: Лекция

Дисциплина: Программная инженерия

Добавлен: 25.10.2018

Просмотров: 7009

Скачиваний: 27

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

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

ОСНОВНЫЕ ОПРЕДЕЛЕНИЯ 

                        

Хотя в тестировании можно  выделить несколько различных процессов, 

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

Нашу классификацию различных форм тестирования мы начнем с того, 

что дадим эти определения, слегка их дополнив и расширив их список. 
     Тестирование  (testing),  как  мы  уже  выяснили,-процесс  выполнения 
программы (или части программы) с намерением (или целью) найти 
ошибки. 
     Доказательство  (proof)    попытка  найти  ошибки  в  программе 
безотносительно к внешней для программы среде. Большинство методов 
доказательства  предполагает  формулировку  утверждений  о  поведении 
программы  и  затем  вывод  и  доказательство  математических  теорем  о 
правильности программы. 
Доказательства  могут  рассматриваться  как  форма  тестирования,  хотя  они  и 
не  предполагают  прямого  выполнения  программы.  Многие  исследователи 
считают  доказательство  альтернативой  тестированию  —  взгляд  во  многом 
ошибочный; более подробно это обсуждается в гл. 17. 
     Контроль (verification)  попытка найти ошибки, выполняя программу в 
тестовой, или моделируемой, среде. 
     Испытание (validation)  попытка найти ошибки, выполняя программу в 
заданной реальной среде. 


background image

     Аттестация (certification)  авторитетное подтверждение правильности 
программы,  аналогичное  аттестации  электротехнического  оборудования 
Underwriters Laboratories. При тестировании с целью аттестации выполняется 
сравнение с некоторым заранее определенным стандартом. 
     Отладка (debugging) не является разновидностью тестирования. Хотя 
слова  «отладка»  и  «тестирование»  часто  используются  как  синонимы,  под 
ними  подразумеваются  разные  виды  деятельности.  Тестирование  — 
деятельность, направленная на обнаружение ошибок; отладка направлена на 
установление точной природы известной ошибки, а затем — на исправление 
этой ошибки. Эти два вида деятельности связаны — результаты тестирования 
являются исходными данными для отладки. 
     Тестирование  модуля,  или  автономное  тестирование  (module  testing, 
unit testing)
  контроль отдельного программного модуля, обычно в 
изолированной среде (т. е. изолированно от всех остальных модулей). 
Тестирование 

модуля 

иногда 

включает 

также 

математическое 

доказательство. 
     Тестирование  сопряжении  (integration  testing)    контроль  сопряжении 
между частями системы (модулями, компонентами, подсистемами). 
     Тестирование  внешних  функций  (external  function  testing)      контроль 
внешнего поведения системы, определенного внешними спецификациями. 
     Комплексное тестирование (system testing)  контроль и/или испытание 
системы  по  отношению  к  исходным  целям.  Комплексное  тестирование 
является процессом контроля, если оно выполняется в моделируемой среде, и 
процессом испытания, если выполняется в среде реальной, жизненной. 
     Тестирование  приемлемости  (acceptance  testing)    проверка  со-
ответствия программы требованиям пользователя. 
     Тестирование  настройки  (installation  testing)  —  проверка  соответствия 
каждого конкретного варианта установки системы с целью выявить 
любые ошибки, возникшие в процессе настройки системы. 
Отношения  между  этими  типами  тестов  и  проектной  документацией,  на 
которой основывается тест, показаны на рис.3, 

               

                

                 

Спектр подходов к проектированию тестов 


background image

               

                

      

Рис. 3. Процессы тестирования и их связь с процессами проектирования.       

   

                         

 

ФИЛОСОФИЯ ТЕСТИРОВАНИЯ   
                    

Тестирование  программного  обеспечения  охватывает  целый  ряд  видов 

деятельности, 

весьма 

аналогичный 

последовательности 

процессов  

разработки  программного  обеспечения.  Сюда  входят  постановка  задачи  для 
теста,  проектирование,  написание  тестов,  тестирование  тестов  и,  наконец, 


background image

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

Сторонник (или сторонница) подхода, соответствующего левой границе 

спектра,  проектирует  свои  тесты,  исследуя  внешние  спецификации  или 
спецификации  сопряжения  программы  или  модуля,  которые  он  тестирует. 
Программу  он рассматривает  как  черный  ящик.  Позиция его  такова:  «Меня 
не интересует, как выглядит эта программа и выполнил ли я все команды или 
все  пути.  Я  буду  удовлетворен,  если  программа  будет  вести  себя  так,  как 
указано в спецификациях». Его идеал — проверить все возможные комбина-
ции и значения на входе. 

Приверженец  подхода,  соответствующего  другому  концу  спектра,  

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

Ни одна из этих крайностей не является хорошей стратегией. Читатель, 

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

Рассмотрим 

попытку 

тестирования 

тривиальной 

программы, 

получающей на входе три числа и вычисляющей их среднее арифметическое. 
Тестирование этой программы для всех значений входных данных невозмож-
но.  Даже  для  машины  с  относительно  низкой  точностью  вычислений 
количество  тестов  исчислялось  бы  миллиардами.  Даже  имей  мы 
вычислительную  мощность,  достаточную  для  выполнения  всех  тестов  в 
разумное  время,  мы  потратили  бы  на  несколько  порядков  больше  времени 
для того, чтобы эти тесты подготовить, а затем проверить. Такие программы, 
как  системы  реального  времени,  операционные  системы  и  программы 
управления  данными,  которые  сохраняют  «память»  о  предыдущих  входных 
данных, еще хуже. Нам потребовалось бы тестировать программу не только 
для  каждого  входного  значения,  но  и  для  каждой  последовательности, 
каждой 

комбинации 

входных 

данных. 

Поэтому 

исчерпывающее 

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


background image

Поскольку  исчерпывающее  тестирование  невозможно,  мы  должны 

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

ИНТЕГРАЦИЯ МОДУЛЕЙ 

 

Вторым  по  важности  аспектом  тестирования  (после  проектирования 

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

ВОСХОДЯЩЕЕ ТЕСТИРОВАНИЕ 

 

При  восходящем  подходе  программа  собирается  и  тестируется  снизу 

вверх.  Только  модули  самого  нижнего  уровня  («терминальные»  модули; 
модули,  не  вызывающие  других  модулей)  тестируются  изолированно, 
автономно. После того как тестирование этих модулей завершено, вызов их 
должен  быть  так  же  надежен,  как  вызов  встроенной  функции  языка  или 
оператор  присваивания.  Затем  тестируются  модули,  непосредственно 
вызывающие  уже  проверенные.  Эти  модули  более  высокого  уровня 
тестируются  не  автономно,  а  вместе  с  уже  проверенными  модулями  более