Тестирование. Ю. Б. Попова тестирование и отладка программного
Скачать 0.6 Mb.
|
2. РАЗРАБОТКА ТРЕБОВАНИЙ К ПРОГРАММНОМУ ПРОДУКТУ Разработка любого программного продукта начинается с опреде- ления требований (англ., Requirements) к нему. Проводятся встречи, интервью с заказчиком, в результате чего составляется документ, в котором отражены все требования к продукту. Там описываются как функциональные (что должна выполнять программа), так и не- функциональные (например, на каком оборудовании должна рабо- тать программа) требования. 2.1. Этапы разработки требований к программному продукту Процесс разработки требований состоит из следующих этапов [6]: 1) Опросы заказчика – проводятся посредством интервью в виде вопросов и ответов. Используются слайды, макеты, чертежи, анало- гичные проекты, обсуждаются пожелания заказчика. В этот момент желательно присутствие тестировщика с целью уточнения способов применения программного продукта, перечня наиболее и наименее 12 используемой функциональности. Такая информация позволит ра- циональнее распределить время, отведенное на тестирование, и уде- лить повышенное внимание самой важной функциональности. 2) Подготовка документа требований. По мере получения сведе- ний от заказчика, требования фиксируются в виде документа (ино- гда его также называют документом определения требований), ко- торый содержит список всех требований на разговорном естествен- ном языке. В этом документе должны быть отражены следующие свойства требований: – каждое требование должно иметь уникальный идентификатор; – требования должны быть представлены с точки зрения поль- зователей системы и не затрагивать внутренние свойства системы и детализацию программного кода; – в перечень должны быть включены как функциональные, так и нефункциональные требования; – документ требований должен храниться в безопасном месте и находиться под управлением конфигурациями. 3) Разработка спецификации требований. Спецификация требо- ваний или спецификация к программному продукту (англ., Software Specification) описывает то же самое, что и документ требований, но предназначен для разработчиков, поэтому содержит уточненные дан- ные и нужные технические детали. Например: используемые библио- теки, классы, БД, типы данных, элементы окон, объекты интерфейса. 4) Построение матрицы прослеживаемости требований (англ., Requirements Traceability Matrix). Назначение этой матрицы – поста- вить в соответствие каждому требованию его реализацию на всех этапах разработки программы, т. е. компоненты проекта, программ- ного кода и тестовые случаи. Такой подход позволяет не потерять ни одно требование ни на каком этапе разработки ПО. 5) Анализ и тестирование требований. После окончания разработ- ки требований приступают к их проверке по следующим критериям: a) полнота – набор требований считается полным, если все его составные части представлены, и каждая часть выполнена в полном объеме. Требования не должны содержать выражений: и так далее, и тому подобное, и прочее, подлежит утонению, а также не должны ссылаться на несуществующую информацию; b) однозначность, т. е. требование должно допускать единствен- ное толкование. Должно быть удобочитаемым и понятным; 13 c) непротиворечивость, т. е. требования не должны противоре- чить друг другу и существующим стандартам. В случае необходи- мости можно вводить систему приоритетов; d) прослеживаемость – каждое требование должно иметь уни- кальный идентификатор, который позволит проследить его на про- тяжении всего жизненного цикла разработки программы; e) осуществимость – каждое требование должно ставить перед системой реально осуществимые задачи как с функциональной точ- ки зрения, так и в смысле затрат времени и средств на разработку; f) контролепригодность – каждое требование должно быть из- меряемым, чтобы тестирование могло выполняться в приемлемых условиях. 2.2. Категории требований к программному продукту Выделяют следующие категории требований к программному продукту [6]: 1) Функциональные средства. Требования этой категории опре- деляют, какие функции должен выполнять данный программный продукт на системном и пользовательском уровне. Для ясности мо- жет быть указано, что не должен выполнять программный продукт. 2) Интерфейсы. Эта категория требований описывает входы, по- лучаемые из внешних систем, и выходы, направляемые во внешние системы, а также указывает, накладываются ли на эти интерфейсы какие-либо ограничения, связанные с форматами данных и носите- лями информации. 3) Данные. Требования этой категории описывают входные и вы- ходные данные системы, какой при этом используется формат, огра- ничения, нужно ли сохранять эти данные, какой объем данных по- ступает в систему, с какой скоростью передачи, с какой точностью должны выполняться вычисления. 4) Производительность. Требования этой категории описывают проблемы масштабирования и синхронизации. Например, количество пользователей, которое одновременно должна обслуживать систе- ма, время ожидания ответа на запрос (следует соблюдать осторож- ность при выражении этих требований в числовых значения, т. к. их необходимо будет проверять). 14 5) Пользователи и человеческий фактор. В этих требованиях опи- сывается квалификация пользователя этой системы, уровень удоб- ства и простоты использования, например, максимальное количе- ство действий для выполнения некоторой операции в системе. 6) Безопасность. Требования этой категории описывают, как осу- ществляется доступ к системе, к ее данным, где данные должны дублироваться и как часто. 7) Документация. Требования определяют, должна ли быть доку- ментация к системе, в печатном или в интерактивном виде, для кого она предназначена. 8) Устранение неисправностей. Требования этой категории опи- сывают, как система должна реагировать на возникновение неис- правностей, надо ли выдавать аварийный сигнал, какое время про- стоя ожидается. 9) Сопровождение. Здесь описываются требования о том, как про- изводится устранения проблем, и каковы условия поставки новых версий программы. 3. МОДУЛЬНОЕ ТЕСТИРОВАНИЕ 3.1. Модульное тестирование и его задачи Модульное тестирование (англ., Unit Testing) – это вид тестовой деятельности, при котором проверке подвергаются внутренние ра- бочие части программы, элементы или модули независимо от спо- соба их вызова. Под модулем принято понимать программу или ограниченную часть кода с одной точкой входа и одной точкой вы- хода, которая выполняет одну и только одну первичную функцию. Этот тип тестирования, как правило, осуществляется программистом, а не тестировщиком, поскольку для его проведения необходимы доскональные знания структуры и кода программы. Тестировать свои собственные продукты – это весьма трудное за- нятие для разработчиков, поскольку они вынуждены изменить свою точку зрения или отношение, перейдя от роли создателя в роль кри- тика. Многие разработчики не любят тщательно тестировать свои программные продукты, так как считают скучным и даже угнетаю- щим наблюдать за тем, как прикладная программа справляется с воз- ложенными на нее задачами. Другие не имеют ничего против подоб- ного подхода и прекрасно выполняют работу по тестированию час- 15 тей программы. Такой подход, несомненно, приводит к обнаружению ошибок на более ранних этапах разработки программного продукта, а это в свою очередь способствует снижению стоимости ошибки. Даже самый опытный программист допускает ошибки. Некоторые исследования показывают, что плотность распределения дефектов находится в диапазоне от 49,5 до 94,6 на тысячу строк кода. Поэтому каждый разработчик должен самостоятельно тестировать свои «про- изведения» с максимальной тщательностью, прежде чем передать рабочий продукт независимому испытателю, т. е. тестировщику. Методы, используемые при модульном тестировании, различа- ются по следующим признакам [7]: a) по степени автоматизации – ручные и автоматизированные методы; b) по форме представления модуля – символьное представление (на языке программирования) или в машинном коде; c) по компонентам программы, на которые направлено тести- рование, – структура программы или преобразование переменных; d) по запуску программы – статические или динамические методы. В первую очередь следует тестировать структуру программы, так как операторы анализа условий составляют в среднем 10–15 % от общего числа операторов программы [7]. Искажение логики ра- боты программы приводит к серьезным ошибкам. К тому же дан- ный вид тестирования имеет наилучшие показатели «эффектив- ность/стоимость». Там же предлагается следующая методика тести- рования модулей: последовательно проводить различные виды тес- тирования, начиная с самых простых: – ручное тестирование (работа за столом); – символическое тестирование (инспекции, сквозные просмотры); – тестирование структуры; – тестирование обработки данных; – функциональное тестирование (сравнение со спецификацией, взаимодействие с другими модулями). Из приведенных выше видов тестирования, ручное и символи- ческое относятся к статическому тестированию, которое проводит- ся без запуска программы. Эти два вида, как правило, основаны на обзорах программного кода, только первый проводится автором- разработчиком, а второй – сторонними специалистами (другими 16 разработчиками, инженерами по качеству или приглашенными инс- пекторами). Последние три из перечисленных видов относятся к динамиче- скому тестированию, и проведение каждого из них состоит из сле- дующих этапов: – планирование тестирования (разработка тестов, формирование контрольных примеров); – собственно тестирование; – обработка результатов тестирования. 3.2. Обзоры программного кода Как уже было сказано, обзоры программного кода являются ос- новой статического тестирования и способны нейтрализовать дей- ствие человеческого фактора при выполнении поставленных задач. При модульном тестировании можно выделить следующие положи- тельные моменты обзоров (учитывая, что оно может проводиться как самим разработчиком, так и сторонними специалистами, кото- рых будем называть экспертами) [8, 9]: 1. Позволяют обнаружить посторонние элементы, что нельзя сделать в ходе традиционного тестирования. Обзоры могут следо- вать по всем выполняемым ветвям разрабатываемого ПО, а с по- мощью динамического тестирования невозможно проверить все ветви. В результате этого, редко проходимые ветви часто приводят к появлению ошибок. 2. Разные точки зрения, личностные качества и жизненный опыт помогают при обнаружении всех видов проблем, которые незамет- ны при поверхностном взгляде. 3. Разработчики могут больше внимания уделять творческому аспекту процесса разработки, зная, что эксперты участвуют в про- екте и действуют в качестве «подстраховки». 4. Происходит разделение труда, благодаря чему эксперты мо- гут сконцентрироваться на этапе обнаружения проблем. 5. Распространение информации и обучение происходят тогда, когда разработчики встречаются при проведении обзоров кода. Лю- ди быстро воспринимают и распространяют хорошие идеи, которые они замечают в работе других. По словам некоторых экспертов – это самый важный результат выполнения обзоров. 17 6. Возрастает степень согласованности между членами команды. Происходит установление фактических групповых норм, что облег- чает понимание сути программных продуктов и их сопровождение. 7. Менеджеры проекта могут получить представление о дейст- вительном состоянии разрабатываемых продуктов. В конечном счете, в результате обзоров программные продукты улучшаются в силу следующих причин: – Проблемы обнаруживаются тогда, когда их можно относитель- но легко исправить без значительных понесенных затрат. – Локализация проблем происходит практически в месте их воз- никновения. – Инспектируются исходные данные какой-либо фазы с целью проверки их соответствия исходным требованиям или критериям выхода. – Предотвращается возникновение дальнейших проблем с по- мощью оглашения решений часто происходящих затруднений. – Распространяются сведения о проекте, что способствует повы- шению его управляемости. – Разработчики обучаются тому, каким образом избежать воз- никновения дефектов при дальнейшей работе. – Предотвращается возникновение дефектов в текущем продук- те, поскольку процесс подготовки материалов для инспекционной проверки способствует уточнению требований и проекта. – Обучаются новые участники проекта. – Менеджерам проекта предоставляются надежные опорные точ- ки и предварительные оценки. – Облегчается поддержка установленного порядка выполнения проекта и обеспечивается объективная, измеримая обратная связь. 3.3. Принципы тестирования структуры программных модулей Как правило, тестирование структуры программного модуля про- исходит с помощью методов графического отображения модуля, од- ним из которых являются ориентированные графы МакКейба [7, 10]. В качестве узлов в графах МакКейба выступают операторы, в каче- стве ребер – связи между операторами (рис. 3.1). 18 Рис. 3.1. Графические конструкции МакКейба [7] Тогда для каждого программного модуля может быть построен граф, который графически отобразит все возможные логические про- ходы по модулю. Проходом (в литературе также встречается термин «маршрут») будем называть путь от первой вершины графа до по- следней. В основных графических конструкциях МакКейба, приве- денных выше, отсутствуют некоторые современные конструкции, например, try-catch-finally. Для ее графического отображения мож- но воспользоваться конструкциями if-then или if-then-else. Метрический показатель сложности или цикломатическое число G потокового графа определяется по формуле G = R – V + 2, (3.1) где R – количество ребер графа; V – количество вершин графа. Следует отметить, что данная формула справедлива только для графа, который начинается с одной вершины и заканчивается одной 19 вершиной. В противном случае цикломатическое число определяют визуально по всем возможным проходам по графу. Чтобы вычислить этот коэффициент вручную для большого и сложно-структурированного программного модуля, потребуется не- мало усилий. К счастью, существуют автоматические средства, ко- торые вычисляют метрический показатель сложности МакКейба путем анализа исходного кода. Метрический показатель сложности не только может пригодиться при установлении проблемных областей, но также может использо- ваться при создании контрольных примеров. Как только потоковый граф создан, очевидными становятся пути, ведущие через програм- мный модуль, которые предоставляют информацию, необходимую для их выполнения в тесте. При планировании тестирования структуры программных моду- лей решаются 2 задачи [7]: 1) формирование критериев выделения маршрутов в программе; 2) выбор стратегии упорядочения выделенных маршрутов. Критерии выделения маршрутов в программе могут быть сле- дующими: a) минимальное покрытие графа программы; b) маршруты, образующиеся при всех возможных комбинациях входящих дуг. Стратегия упорядочения маршрутов выбирается по следующим параметрам: – длительности исполнения и числу команд в маршрутах. Такая стратегия выбирается при тестировании программ вычислительного характера; – количеству условных переходов, определяющих формирование данного маршрута. Такая стратегия выбирается при тестировании логических программ с небольшим объемом вычислений, а также в тех случаях, когда сложно оценить вероятность ветвления и коли- чество исполнений циклов. 3.4. Способы тестирования взаимодействия модулей После проведения модульного тестирования необходимо прове- рить совместную работу программных модулей, т. е. провести инте- 20 грационное тестирование. Выделяют два способа проверки взаимо- действия модулей [11]: 1) монолитное тестирование; 2) пошаговое тестирование. Пусть имеется программа, состоящая из нескольких модулей, представленных на рис. 3.2. Главным модулем программы является модуль А, требуемые данные в программу попадают через модуль J, а выводятся на экран через модуль I. A C D B I E G F H J Рис. 3.2. Схема многомодульной программы Обозначения на рис. 3.2 следующие: прямоугольники – это про- граммные модули, тонкие линии представляют иерархию управле- ния (связи по управлению между модулями), жирные стрелки – ввод и вывод данных в программу. Монолитное тестирование [11] заключается в том, что каждый модуль тестируется отдельно. Для каждого модуля пишется один модуль-драйвер, который передает тестируемому модулю управле- ние, и один или несколько модулей-заглушек. Например, для моду- ля B нужны 2 заглушки, имитирующие работу модулей Е и F. Когда все модули протестированы, они собираются вместе и тестируется вся программа целиком. Пошаговое тестирование предполагает, что модули тестируют- ся не изолированно, а подключаются поочередно к набору уже от- тестированных модулей. 21 Можно выделить следующие недостатки монолитного тестиро- вания (перед пошаговым) [7, 11]: 1. Требуется много дополнительных действий (написание драй- веров и заглушек). 2. Поздно обнаруживаются ошибки в межмодульных взаимо- действиях. 3. Следствие из 2 – труднее отлаживать программу. К преимуществам монолитного тестирования можно отнести: 1. Экономию машинного времени (в настоящее время сущест- венной экономии не наблюдается) 2. Возможность параллельной организации работ на начальной фазе тестирования. 3.5. Стратегии выполнения пошагового тестирования Существует две принципиально различные стратегии выполне- ния пошагового тестирования [7, 11]: 1) нисходящее тестирование; 2) восходящее тестирование. Нисходящее тестирование начинается с главного модуля, в на- шем случае с модуля А. Возникают проблемы: как передать тесто- вые данные в А, ведь ввод и вывод осуществляется в других моду- лях? Как передать в А несколько тестов? Решение можно представить в следующем виде: а) написать несколько вариантов заглушек модуля B (для каждо- го теста); б) написать заглушку B так, чтобы она читала тестовые данные из внешнего файла. В качестве стратегии подключения модулей можно использовать одну из следующих: – подключаются наиболее важные с точки зрения тестирования модули; – подключаются модули, осуществляющие операции ввода/вы- вода (для того, чтобы обеспечивать тестовыми данными «внутрен- ние» модули). Нисходящее тестирование имеет ряд недостатков: предположим, что модули I и J уже подключены, и на следующем шаге тестиро- вания заглушка H меняется на реальный модуль H. Как передать 22 тестовые данные в Н? Это нетривиальная задача, потому что между J и Н имеются промежуточные модули, и может оказаться невоз- можным передать тестовые данные, соответствующие разработан- ным тестам. К тому же, достаточно сложно интерпретировать ре- зультаты тестирования Н, так как между Н и I также существуют промежуточные модули. Восходящее тестирование практически полностью противопо- ложно нисходящему тестированию. Начинается с терминальных (не вызывающих другие модули) модулей. А стратегия подключения новых модулей также основывается на степени критичности данно- го модуля в программе. Восходящее тестирование лишено недо- статков нисходящего тестирования, однако имеет свой главный не- достаток: рабочая программа не существует до тех пор, пока не до- бавлен последний (в нашем случае А) модуль. Выбор одной из двух представленных стратегий определяется тем, на какие модули (верхнего или нижнего уровня) следует обратить внимание при тестировании в первую очередь. |