Процессы тестирования структуры программных компонентов

Как отмечалось выше (см. п. 13.1), оценивание корректности программных средств можно представить двумя видами работ:

— верификацией — последовательным прослеживанием сверху вниз реализации требований к системе и ПС программными компонентами нижних уровней;

— определением полноты покрытия тестами их структуры и проверками выполнения исходных требований к ПС и его компонентам.


Лекция 13. Верификация, тестирование и оценивание корректности компонентов

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

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

На практике при отсутствии упорядоченного анализа потоков управления некоторые маршруты в программе оказываются пропущенными при тестировании (до 50%), поэтому первая задача, которая должна решаться при тестировании структуры программ, — это получение информации о полной совокупности реальных маршрутов исполнения в каждой программе и ее структурной сложности. Такое представление покрытия программы позволяет упорядоченно контролировать достигнутую проверку


13.4. Процессы тестирования структуры программных компонентов

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

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

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

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

Xj — покрытие графа программы минимальным количеством маршрутов, охватывающих каждую дугу графа хотя бы один раз;

Х2 — выделение всех линейно независимых маршрутов, отличающихся хотя бы одной дугой в маршруте от остальных;

Х3 — выделение маршрутов при всех возможных комбинациях дуг, входящих в маршруты.

Планировать тестирование можно по одному из критериев или используя последовательно более жесткие критерии выделения маршрутов, при которых возрастают соответственно объем и сложность тестирования.


Лекция 13. Верификация, тестирование и оценивание корректности компонентов

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

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

Упорядочение маршрутов при планировании тестирования базируется на использовании в основном трех характеристик программных модулей:

— стратегия 1 учитывает число строк текста программы в выделенных маршрутах или расчетную длительность их исполнения при функционировании программы;

— стратегия 2 анализирует число альтернатив или условных переходов, определяющих образование каждого маршрута;

— стратегия 3 базируется на использовании вероятности исполнения маршрутов при реальном функционировании программы.

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

При стратегии 2 приоритет отдается маршрутам, наиболее сложным по числу анализируемых условий. Такая стратегия предпочтительна при тестировании логических программ с небольшим объемом вычисле-


13.4. Процессы тестирования структуры программных компонентов

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

При упорядочении маршрутов по стратегии 3 основная сложность состоит в оценке и учете вероятностей ветвления в условных переходах и переключателях, а также числа исполнения циклов. Их значения должны указываться разработчиками программ, что достаточно трудоемко и субъективно. Тем не менее такая стратегия позволяет наиболее детально планировать тестирование и оценивать предельный уровень корректности программ.

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

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


Лекция 13. Верификация, тестирование и оценивание корректности компонентов


можно оценивать по числу маршрутов Мх, необходимых для их проверки, или более полно по суммарному числу условий Ех, которое необходимо задать в тестах для прохождения всех маршрутов программы, выделенных по Х-му критерию:

м х

£* = ££,• 03.1)

где Е i — число условий-предикатов, определяющих /-й маршрут. Маршруты исполнения программного модуля можно разделить на два вида:

— маршруты исполнения преимущественно вычислительной части программы и преобразования непрерывных переменных;

— маршруты принятия логических решений и преобразования логических переменных.

Маршруты первого вида обычно логически проще и короче, чем второго, и предназначены для преобразования величин, являющихся квантованными результатами измерения некоторых непрерывных физических характеристик (непрерывные переменные). Значения таких переменных связаны условиями гладкости, т.е. условиями малых изменений производных этих переменных по времени или по другим параметрам. При оценке сложности вычислительных маршрутов программ необходим учет числа операндов, участвующих в вычислениях. Кроме того, исходные и результирующие данные при тестировании должны принимать несколько значений. Во всем диапазоне исходных переменных следует выбирать несколько характерных точек (предельные значения и несколько промежуточных), при которых проверяется программа. В особых точках значений и сочетаний переменных и в точках разрыва функции необходимо планировать дополнительные проверки. Таким образом, сложность проверки программного модуля будет определяться числом маршрутов Мх исполнения программы и числом обрабатываемых операндов /, на каждом /-м маршруте, умноженном на число значений s/7 для каждой исходной у'-й величины на этом маршруте:

My /;

S
x=2jZ,uV (13.2) '=1 7=1

= 1±'г


13.4. Процессы тестирования структуры программных компонентов

Расчет показателя сложности тестирования программного модуля по такой схеме имеет значительную неопределенность из-за произвола в выборе числа значений яи (в основном особых точек) при варьировании исходных данных в тесте. В то же время доля вычислительной части во многих сложных ПС относительно невелика.

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

Структурная сложность программного модуля может быть рассчитана по числу маршрутов Мх в программе и сложности каждого /-го маршрута Е(. Эти показатели в совокупности определяют минимальную сложность £*-тестов для проверки программного модуля (13.1), а следовательно, трудоемкость его разработки и тестирования и вероятность пропуска логической ошибки в программе. Программные модули являются наиболее массовыми компонентами в ПС и требуют для тестирования суммарно наибольших затрат ресурсов. Затраты на тестирование каждого модуля прямо пропорциональны сложности, которая зависит от его структуры и объема вычислений /,. При тестировании программного модуля необходимо задать и проанализировать число значений параметров:

1=1 7=1

Суммарные затраты ресурсов на тестирование модуля пропорциональны значению его сложности Вх и, с учетом удельных затрат на создание каждого теста — с, определяются выражением:

С*=СХ(Х^+^)' (13.4)

/=1 7=1


Лекция 13. Верификация, тестирование и оценивание корректности компонентов

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

Сложность тестирования программ, содержащих циклы. Наличие циклов в программе способно резко увеличивать сложность их тестирования. Полное, исчерпывающее тестирование должно охватывать проверку каждого маршрута в цикле при всех возможных итерациях цикла и при всех сочетаниях циклов с маршрутами ациклической части программы. Предположим для простоты, что число маршрутов в нижней ациклической части программы равно Мъ = 1. Тогда полное множество маршрутов М состоит из полной совокупности всех маршрутов Мх в верхней ациклической части программы и группы маршрутов М2, в которой к каждому маршруту из Мх присоединено 1..2..3... итерации (витка) цикла. При этом на каждой итерации выполняется, по крайней мере, один из внутренних маршрутов тела цикла.

Например, для графа, имеющего один цикл, требующего исполнения пяти итераций (витков) с тремя внутренними маршрутами, а также содержащего Mi = 10 ациклических маршрутов, проходящих через цикл, суммарное число маршрутов для исчерпывающего тестирования равно М = (3 х 5) х 10 = 150. При возрастании любого из сомножителей (числа независимых ациклических маршрутов, проходящих через цикл, числа внутренних маршрутов тела цикла или числа его итераций) пропорционально растет их произведение, а следовательно, и сложность тестирования. Поэтому исчерпывающее тестирование реальных сложных программ с циклами может быть практически невозможно.

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

— на разных итерациях цикла исполняются независимо все возможные маршруты тела цикла;


13.4. Процессы тестирования структуры программных компонентов

— на всех итерациях цикла исполняется один и тот же маршрут тела цикла или некоторая определенная их последовательность;

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

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

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


Лекция 13. Верификация, тестирование и оценивание корректности компонентов

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

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

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

Сложность тестирования программных компонентов (функциональных групп программ) определяется суммарной сложностью модулей и межмодульных связей по управлению и по информации. Каждый модуль должен тестироваться автономно до включения в группу программ и частично в составе группы. Затраты на тестирование модулей в составе


13.5. Примеры оценок сложности тестирования программ

группы программ должны учитывать относительные суммарные затраты на тестирование всех входящих модулей с коэффициентом dk<\, зависящим от степени предшествующей проверки модуля. Если модули автономно не тестировались (например, при нисходящем тестировании), то dk = 1 и затраты на тестирование каждого модуля войдут в затраты при тестировании группы программ в полном объеме. При тщательном автономном тестировании модулей можно полагать dk = 0,1—0,01, т.е. в составе ПС затраты на тестирование каждого из модулей составляют несколько процентов.