Text
                    ОСНОВЫ ИНФОРМАЦИОННЫХ
ТЕХНОЛОГИЙ

В. П. Котляров
Т. В. Коликова

ОСНОВЫ ТЕСТИРОВАНИЯ

ПРОГРАММНОГО

ОБЕСПЕЧЕНИЯ

NatattausA

знание без границ Ч

УДК 004.415.53(075.8) ББК 32.973.26-018.2я73 К73 Котляров В. П. К73 Основы тестирования программного обеспечения: Учебное пособие / В. П. Котляров, Т. В. Коликова — М.: Интернет- Университет Информационных Технологий; БИНОМ. Лаборато- рия знаний, 2006. - 285 с.: ил.(Серия «Основы информационных технологий»). ISBN 5-9556-0027-2 (ИНТУИТ) ISBN 5-94774-406-4 (БИНОМ. ЛЗ) Курс посвящен обсуждению проблем контроля качества разработки про- граммного обеспечения с позиций тестирования. Задачей курса, реализующейся через лекционный материал и практикум, является подготовка тестировщиков программного проекта. Для студентов высших учебных заведений, обучающихся по специальностям в области информационных технологий. УДК 004.415.53(075.8) ББК 32.973.26-018.2я73 Издание осуществлено при финансовой и технической поддержке издательства «Открытые Системы», «РМ Телеком» и Kraftway Computers. Полное или частичное воспроизведение или размножение каким-либо способом, в том числе и публикация в Сети, настоящего издания допускается только с письменного разрешения Интернет-Университета Информационных Технологий. По вопросам приобретения обращаться: «БИНОМ. Лаборатория знаний» Телефон: (495) 157-19-02, 157-52-72, e-mail: Lbz@aha.ru, http://www.Lbz.ru ISBN 5-9556-0027-2 (ИНТУИТ) ISBN 5-94774-406-4 (БИНОМ. ЛЗ) © Интернет-Университет Информационных Технологий. 2006
О проекте Интернет-Университет Информационных Технологий — это первое в России высшее учебное заведение, которое предоставляет возможность получить дополнительное образование во Всемирной сети. Web-сайт уни- верситета находится по адресу www.intuit.ru. Мы рады, что вы решили расширить свои знания в области компью- терных технологий. Современный мир — это мир компьютеров и инфор- мации. Компьютерная индустрия — самый быстрорастущий сектор эконо- мики, и ее рост будет продолжаться еще долгое время. Во времена жесткой конкуренции от уровня развития информационных технологий, достиже- ний научной мысли и перспективных инженерных решений зависит успех не только отдельных людей и компаний, но и целых стран. Вы выбрали са- мое подходящее время для изучения компьютерных дисциплин. Профес- сионалы в области информационных технологий сейчас востребованы везде: в науке, экономике, образовании, медицине и других областях, в го- сударственных и частных компаниях, в России и за рубежом. Анализ дан- ных, прогнозы, организация связи, создание программного обеспечения, построение моделей процессов — вот далеко не полный список областей применения знаний для компьютерных специалистов. Обучение в университете ведется по собственным учебным планам, разработанным ведущими российскими специалистами на основе между- народных образовательных стандартов Computer Curricula 2001 Computer Science. Изучать учебные курсы можно самостоятельно по учебникам или на сайте Интернет-университета, задания выполняется только на сайте. Для обучения необходимо зарегистрироваться на сайте университета. Удостоверение об окончании учебного курса или специальности выдает- ся при условии выполнения всех заданий к лекциям и успешной сдачи итогового экзамена. Книга, которую вы держите в руках — очередная в многотомной серии «Основы информационных технологий», выпускаемой Интернет- Университетом Информационных Технологий. В этой серии будут выпущены учебники по всем базовым областям знаний, связанным с компьютерными дисциплинами. Добро пожаловать в Интернет-Университет Информационных Технологий! Анатолий Шкред anatoli@shkred.ru
Об авторе Котляров Всеволод Павлович — кандидат технических наук, профес- сор Санкт-Петербургского государственного политехнического универ- ситета, ведущий научный сотрудник. Работает в области обучения профессиональному программирова- нию на кафедре информационных и управляющих систем Санкт-Петер- бургского государственного политехнического университета более ЗОлет, внес большой вклад в формирование учебного процесса специальности «Программное обеспечение вычислительной техники и автоматизиро- ванных систем» на кафедре, автор более 200 научных трудов. Коликова Татьяна Всеволодовна старший преподаватель кафедры ин- формационных и управляющих систем факультета технической киберне- тики Санкт-Петербургского государственного политехнического универ- ситета. Читает курсы по теории операционных систем и разработке прило- жений. Пять студенческих проектов, выполненных под ее научным руко- водством, стали призерами конкурсов-конференций организованных Microsoft.
СОДЕРЖАНИЕ Лекция 1. Введение: тестирование — способ обеспечения качества программного продукта......................................9 Требования к курсу....................................11 Основные темы лекционного курса.......................11 Основные темы практикума..............................12 Прогнозируемые результаты.............................13 Потребители курса.....................................13 Лекция 2. Основные понятия тестирования...................15 Концепция тестирования....................................15 Основная терминология.....................................16 Организация тестирования..............................18 Три фазы тестирования.................................26 Простой пример........................................26 Управляющий граф программы............................27 Основные проблемы тестирования........................28 Лекция 3. Критерии выбора тестов..........................31 Критерии выбора тестов................................31 Требования к идеальному критерию тестирования.........31 Классы критериев......................................31 Структурные критерии (класс I)...................... 32 Функциональные критерии (класс II)....................34 Стохастические критерии (класс III)...................37 Мутационный критерий (класс IV).......................40 Лекция 4. Оценка оттестированности проекта: метрики и методика интегральной оценки.......................................43 Оценка Покрытия Программы и Проекта...................43 Методика интегральной оценки тестированности..........50 Лекция 5. Модульное и интеграционное тестирование.........51 Разновидности тестирования............................51 Модульное тестирование................................51 Интеграционное тестирование...........................57
Лекция 6. Интеграционное тестирование и его особенности для объектно-ориентированного программирования..................63 Лекция 7. Разновидности тестирования: системное и регрессионное тестирование................................................73 Системное тестирование..................................73 Регрессионное тестирование..............................77 Комбинирование уровней тестирования.....................79 Лекция 8. Автоматизация тестирования........................81 Автоматизация тестирования..............................81 Издержки тестирования...................................84 Лекция 9. Особенности индустриального тестирования..........86 Индустриальный подход...................................86 Особенности индустриального тестирования................86 Качество программного продукта и тестирование...........86 Процесс тестирования....................................90 Планирование тестирования...............................92 Подходы к разработке тестов.............................94 Лекция 10. Документирование и оценка индустриального тестирования................................................99 Выполнение тестов.......................................99 Документация и сопровождение тестов....................102 Оценка качества тестов.................................105 Лекция 11. Регрессионное тестирование: цели и задачи, условия применения, классификация тестов и методов отбора..........108 Цели и задачи регрессионного тестирования..............108 Виды регрессионного тестирования........................НО Управляемое регрессионное тестирование.................112 Обоснование корректности метода отбора тестов..........114 Классификация тестов при отборе........................116 Возможности повторного использования тестов............118 Классификация выборочных методов.......................121 Лекция 12. Регрессионное тестирование: разновидности методов отбора тестов..............................................124 Случайные методы.......................................124 Безопасные методы......................................125 Методы минимизации.....................................127
Методы, основанные на покрытии кода....................129 Лекция 13. Регрессионное тестирование: методики, не связанные с отбором тестов и методики порождения тестов................131 Интеграционное регрессионное тестирование............. 131 Регрессионное тестирование объектно-ориентированных программ...............................................132 Уменьшение объема тестируемой программы................132 Методы упорядочения....................................134 Целесообразность отбора тестов.........................136 Функции предсказания целесообразности..................140 Порождение новых тестов................................143 Лекция 14. Регрессионное тестирование: алгоритм и программная система поддержки..........................................146 Методика регрессионного тестирования...................146 Система поддержки регрессионного тестирования..........147 Практикум по тестированию программного обеспечения Семинар 1. Описание тестируемой системы и ее окружения. Планирование тестирования..................................155 Семинар 2. Модульное тестирование на примере классов.......164 Семинар 3. Интеграционное тестирование.....................173 Семинар 4. Системное тестирование..........................179 Семинар 5. Ручное тестирование.............................186 Семинар 6. Автоматизация тестирования с помощью скриптов...192 Семинар 7. Автоматическая генерация тестов на основе формального описания ................................................. 199 Семинар 8. Описание ручного тестирования...................204 Семинар 9. Автоматизация тестирования с помощью скриптов...206 Семинар 10. Описание автоматической генерации MSC..........209 Семинар 11. Использование MS Visio для генерации МPR-файлов .. 217 Приложение 1. Руководство по подготовке компьютерного класса .. 234 Приложение 2. Функциональная спецификация..................239 Приложение 3. Высокоуровневый дизайн.......................253 Литература.................................................270 Приложение.................................................272
Внимание! На сайте Интернет-университета информационных технологий Вы можете пройти тестирование по каждой лекции и курсу в целом. Добро пожаловать на наш сайт: www.intuit.ru
Лекция 1. Введение: тестирование - способ обеспечения качества программного продукта Рассмотрена проблематика, цели и требования к курсу. Обсуждены ос- новные темы курса и практикума. Ключевые слова: тестирование, качество программного продукта, требования к курсу, темы курса и практикума, потребители курса. Качество программного продукта характеризуется набором свойств, определяющих, насколько продукт «хорош» с точки зрения заинтересо- ванных сторон, таких как заказчик продукта, спонсор, конечный пользо- ватель, разработчики и тестировщики продукта, инженеры поддержки, сотрудники отделов маркетинга, обучения и продаж. Каждый из участни- ков может иметь различное представление о продукте и о том, насколько он хорош или плох, то есть о том, насколько высоко качество продукта. Таким образом, постановка задачи обеспечения качества продукта выли- вается в задачу определения заинтересованных лиц, их критериев качест- ва и затем — нахождения оптимального решения, удовлетворяющего этим критериям. Тестирование является одним из наиболее устоявшихся спо- собов обеспечения качества разработки программного обеспечения и входит в набор эффективных средств современной системы обеспечения качества программного продукта. С технической точки зрения тестирование заключается в выполне- нии приложения на некотором множестве исходных данных и сверке по- лучаемых результатов с заранее известными (эталонными) с целью уста- новить соответствие различных свойств и характеристик приложения за- казанным свойствам. Как одна из основных фаз процесса разработки программного продукта (Дизайн приложения — Разработка кода — Тести- рование), тестирование характеризуется достаточно большим вкладом в суммарную трудоемкость разработки продукта. Широко известна оценка распределения трудоемкости между фазами создания программного про- дукта: 40%-20%-40% (рис. 1.1), из чего следует, что наибольший эффект в снижении трудоемкости может быть получен прежде всего на фазах Design и Testing. Поэтому основные вложения в автоматизацию или гене- рацию кода следует осуществлять, прежде всего, на этих фазах. Хотя в со- временном индустриальном программировании автоматизация тестиро- вания является широко распространенной практикой, в то же время тех- нология верификации требований и спецификаций пока делает только свои первые шаги. Задачей ближайшего будущего является движение в сторону такого распределения трудоемкости (60%-20%-20% (рис. 1.2)),
чтобы суммарная цена обнаружения большинства дефектов стремилась к минимуму за счет обнаружения преимущественного числа на наиболее ранних фазах разработки программного продукта. Настоящий курс посвящен обсуждению способов решения задачи контроля качества разработки программного обеспечения с позиций тес- тирования. В этой области, наряду с решением научных и технических проблем, немаловажная роль принадлежит проблеме подготовки кадров, способных решать задачи тестирования и автоматизации тестирования в Рис. 1.1. Оценка трудоемкости обнаружения и исправления ошибок при создании программного продукта Разработка спецификаций Кодирование Тестирование и требований Рис. 1.2. Аналогичная оценка при автоматизации дизайна
условиях производства программного продукта. Задачей курса, реализую- щейся через лекционный материал и практикум, является подготовка те- стировщиков программного проекта. Это тем более важно, что в сущест- вующих вузовских программах подготовки профессиональных програм- мистов не предусмотрен достаточный для решения данной задачи объем лекционного материала и практикумов. Поэтому предлагаемое пособие следует рассматривать как дополнительный учебник для будущих тести- ровщиков программных проектов. Предлагаемый вниманию читателей курс обобщает опыт многолет- ней работы учебного центра «Политехник — Моторола» в Санкт-Петер- бургском государственном политехническом университете. Естественно, наш учебник не единственный. Среди учебников, посвященных подготовке тестировщиков, мы ре- комендуем обратить внимание на книги [1 ],[2],[3],[4],[5],[6], также по- священные передаче опыта промышленного тестирования студентам и аспирантам, выбравшим своей специальностью профессиональное про- граммирование. Требования к курсу Курс соответствует требованиям специальности 220400 «Программ- ное обеспечение вычислительной техники и автоматизированных сис- тем», ориентированной на подготовку профессиональных программи- стов, в частности покрывает разделы курса «Технология программирова- ния», посвященные тестированию. Разделы курса соответствуют следующим разделам Computing Curricula 2001: Computer Science [7]: • раздел SE4 «Процессы разработки ПО»; • раздел SE5 «Спецификации и требования к ПО»; • раздел SE6 «Проверка соответствия ПО». Основные темы лекционного курса • Основные понятия тестирования: терминология тестирования, различия тестирования и отладки, фазы и технология тестирова- ния, проблемы тестирования. • Критерии выбора тестов: структурные, функциональные, стохас- тические, мутационный, оценки покрытия проекта. • Разновидности тестирования: модульное, интеграционное, сис- темное, регрессионное, автоматизация тестирования, издержки тестирования. • Особенности процесса и технологии индустриального тестирова-
ния: планирование тестирования, подходы к разработке тестов, особенности ручной разработки и генерации тестов, автоматиза- ция тестового цикла, документирование тестирования, обзоры и метрики. • Регрессионное тестирование: особенности и виды регрессионно- го тестирования, методы отбора тестов, оценка эффективности. • Терминологический словарь: содержит глоссарий терминологии тестирования в соответствии с IEEE Standard Glossary of Software Engineering [8],[9]. В курсе использованы примеры, разработанные на языке С#. Для читателей, не владеющих С#, эти же примеры продублированы на С.С++ в Приложении. Основные темы практикума Для демонстрации и закрепления теоретических знаний разработан практикум, содержащий: • описание практических работ (для студентов); • методические указания по проведению практических работ (для преподавателей); • рекомендации по подготовке компьютерной лаборатории к про- ведению практических работ. В рамках практикума студенты осваивают различные подходы к раз- работке тестов и тестированию и условия их применения. Практикум представлен в форме тренинга, в котором рассмотрены следующие темы: • Разработка документации на тестируемую систему и ее окруже- ние: описание требований (Requirement Specification) и специфи- каций разработчика (High Level Design). • Планирование тестирования. • Практикум модульного тестирования. • Практикум интеграционного тестирования. • Практикум системного тестирования. • Ручное тестирование и тестовые процедуры. • Автоматизированное тестирование на основе скриптов. • Автоматизированное тестирование на основе MSC-диаграмм и генерация тестов. • Средства поддержки автоматизации тестирования. Используя модель реальной системы управления, студенты могут: • разрабатывать различные виды тестов и тестирующих программ; • искать дефекты системы в процессе тестирования, участвовать в
их исправлении и модернизации тестируемого приложения; • разрабатывать документацию — требования к системе, тесты и те- стовые процедуры — и отслеживать взаимосвязь этих документов с разработанными тестами. Прогнозируемые результаты В результате изучения курса: 1. Вырабатывается понимание условий применения Верификации, Валидации и Тестирования. 2. Вырабатываются навыки и приемы тестирования, применяемые на различных фазах разработки качественного программного про- дукта. 3. Оцениваются условия эффективного применения инструменталь- ных средств в разработке качественного программного обеспече- ния. 4. Вырабатываются навыки разработки тестовых программ и тесто- вых наборов в программном проекте. 5. Вырабатываются навыки разработки проектной документации для этапа тестирования. 6. Вырабатываются навыки планирования и отслеживания задач те- стирования. 7. Обеспечиваются основы обучения проектной команды, состоя- щей из разработчиков и тестировщиков. 6. Вырабатываются навыки тестирования программного обеспече- ния проектов, разработанных на С#. Потребители курса Курс и практикум рассчитаны на студентов следующих программи- стских специальностей: • 220400 «Программное обеспечение вычислительной техники и автоматизированных систем». • 220200 «Программное обеспечение автоматизированных систем управления». • 220300 «Системы автоматизации проектирования». • 351500 «Математическое обеспечение и администрирование ин- формационных систем». • На студентов других специальностей, желающих получить знания и навыки, необходимые для работы в области промышленного те- стирования программных продуктов.
Курс Основы тестирования программного обеспечения Благодарности Авторы выражают искреннюю благодарность Московскому отделе- нию Microsoft Corporation, спонсировавшему разработку настоящего по- собия, и лично Люцареву В.С., отметившему своевременность и полез- ность данной работы. Активное участие в подготовке курса принимали аспиранты А. Не- красов и Н. Епифанов, чьи диссертационные материалы были использо- ваны при написании 5-й и 6-й глав. Создание настоящего пособия было бы невозможно без самоотвер- женной работы студенческого коллектива, выполнившего разработку и проверку всех примеров. Коллектив в составе студентов 4-го курса К. Кудряшева, Д. Пескова, М. Даишева, Е. Марченкова и его руководите- ля аспиранта Д. Югая, был организован в виде программистской бригады и вел разработку по законам, используемым в промышленных проектах.
Лекция 2. Основные понятия тестирования Рассмотрены подходы к обоснованию истинности формул и программ и их связь с тестированием. Представлены на конкретных примерах понятия отладки и тестирования. Рассмотрены вопросы организации тестирования. На примерах пояснены методы поиска ошибок и процедура тестирования. Рассмотрены фазы тестирования, основные проблемы тестирования и поста- влена задача выбора конечного набора тестов. Ключевые слова: истинность формул, отладки, тестирование, орга- низация тестирования, методы поиска ошибок, процедура тестирования, оракул, дамп, точка останова, фазы тестирования, проблемы тестирова- ния, набор тестов, прогон тестов, управляющий граф, путь, ветвь, выбор конечного набора тестов. Концепция тестирования Программа — это аналог формулы в обычной математике. Формула для функции f, полученной суперпозицией функций fj, f*2» — fn — выражение, описывающее эту суперпозицию: f=f1*f2*f3*-*fn Если аналог f'|, f2,..., fn — операторы языка программирования, то их формула — программа. Существует два метода обоснования истинности формул: 1. Формальный подход или доказательство применяется, когда из ис- ходных формул-аксиом с помощью формальных процедур (пра- вил вывода) выводятся искомые формулы и утверждения (теоре- мы). Вывод осуществляется путем перехода от одних формул к другим по строгим правилам, которые позволяют свести процеду- ру перехода от формулы к формуле к последовательности тексто- вых подстановок: А**3 = А*А*А А*А*А = А -» R, A*R -> R, A*R -> R Преимущество формального подхода заключается в том, что с его помощью удается избегать обращений к бесконечной области значений, и на каждом шаге доказательства оперировать только конечным множеством символов.
2. Интерпретационный подход применяется, когда осуществляется подстановка констант в формулы, а затем — интерпретация фор- мул как осмысленных утверждений в элементах множеств кон- кретных значений. Истинность интерпретируемых формул прове- ряется на конечных множествах возможных значений. Сложность подхода состоит в том, что на конечных множествах комбинации возможных значений для реализации исчерпывающей проверки могут оказаться достаточно велики. Интерпретационный подход используется при эксперименталь- ной проверке соответствия программы своей спецификации. Применение интерпретационного подхода в форме экспериментов над исполняемой программой составляет суть отладки и тестирования. Основная терминология Отладка (debug, debugging) — процесс поиска, локализации и испра- вления ошибок в программе [9] [IEEE Std.610-12.1990]. Термин «отладка» в отечественной литературе используется двояко: для обозначения активности по поиску ошибок (собственно тестирова- ние), по нахождению причин их появления и исправлению, или активно- сти по локализации и исправлению ошибок. Тестирование обеспечивает выявление (констатацию наличия) фак- тов расхождений с требованиями (ошибок). Как правило, на фазе тестирования осуществляется и исправление идентифицированных ошибок, включающее локализацию ошибок, нахо- ждение причин ошибок и соответствующую корректировку программы тестируемого приложения (Application (AUT) или Implementation Under Testing (ШТ)). Если программа не содержит синтаксических ошибок (прошла трансляцию) и может быть выполнена на компьютере, она обязательно вычисляет какую-либо функцию, осуществляющую отображение вход- ных данных в выходные. Это означает, что компьютер на своих ресурсах доопределяет частично определенную программой функцию до тоталь- ной определенности. Следовательно, судить о правильности или непра- вильности результатов выполнения программы можно, только сравнивая спецификацию желаемой функции с результатами ее вычисления, что и осуществляется в процессе тестирования. Пример поиска и исправления ошибки Отладка обеспечивает локализацию ошибок, поиск причин ошибок и со- ответствующую корректировку программы (рис. 2.1, рис. 2.2).
// Метод вычисляет неотрицательную степень п числа х static public double Power(double x, int n) { double z=l; for (int i=l;n>=i;i++) { z - z*x; } return z; } Рис. 2.1. Исходный текст метода Power Если вызвать метод Power с отрицательным значением степени п Power(2,-1), то получим некорректный результат — 2. Исправим метод так, чтобы ошибочное значение параметра (недопустимое по специфи- кации значение) идентифицировалось специальным сообщением, а возвращаемый результат был равен 1 (рис. 2.2). // Метод вычисляет неотрицательную степень п числа х static public double PowerNonNeg(double x, int n) { double z=l; if (n>0) { for (int i=l;n>=i;i++) > { z - z*x; } } else Console.WriteLine("Ошибка! Степень числа n должна быть больше 0."); return z; } Рис. 2.2. Скорректированный исходный текст Если вызвать скорректированный метод PowerNonNeg(2,-l) с отрица- тельным значением параметра степени, то сообщение об ошибке будет выдано автоматически.
Судить о правильности или неправильности результатов выполне- ния программы можно только сравнивая спецификацию желаемой функ- ции с результатами ее вычисления. Тестирование разделяют на статическое и динамическое. Статиче- ское тестирование выявляет формальными методами анализа без выпол- нения тестируемой программы неверные конструкции или неверные от- ношения объектов программы (ошибки формального задания) с помо- щью специальных инструментов контроля кода — CodeCheker. Динамическое тестирование (собственно тестирование) осуществля- ет выявление ошибок только на выполняющейся программе с помощью специальных инструментов автоматизации тестирования — Testbed [9] или Testbench. Организация тестирования Тестирование осуществляется на заданном заранее множестве вход- ных данных X и множестве предполагаемых результатов Y — (X,Y), кото- рые задают график желаемой функции. Кроме того, зафиксирована про- цедура Оракул (oracle), которая определяет, соответствуют ли выходные данные — Yb (вычисленные по входным данным — X) желаемым результа- там — Y, т.е. принадлежит ли каждая вычисленная точка (х,ув) графику желаемой функции (X,Y). Оракул дает заключение о факте появления неправильной пары (х,ув) и ничего не говорит о том, каким образом она была вычислена или каков правильный алгоритм — он только сравнивает вычисленные и же- лаемые результаты. Оракулом может быть даже Заказчик или програм- мист, производящий соответствующие вычисления в уме, поскольку Ора- кулу нужен какой-либо альтернативный способ получения функции (X,Y) для вычисления эталонных значений Y. Пример сравнения словесного описания пункта спецификации с результатом выполнения фрагмента кода Пункт спецификации: «Метод Power должен принимать входные пара- метры: х — целое число, возводимое в степень, и п — неотрицательный порядок степени. Метод должен возвращать вычисленное значение х^1». Выполняем метод со следующими параметрами: Power(2,2) Проверка результата выполнения возможна, когда результат вычисле- ния заранее известен — 4. Если результат выполнения 22 = 4, то он со- ответствует спецификации. В процессе тестирования Оракул последовательно получает элемен-
ты множества (X,Y) и соответствующие им результаты вычислений (X,Yb) для идентификации фактов несовпадений (test incident). При выявлении (х,ув) (X,Y) запускается процедура исправления ошибки, которая заключается во внимательном анализе (просмотре) про- токола промежуточных вычислений, приведших к (х,ув), с помощью сле- дующих методов: 1) «Выполнение программы в уме» (deskchecking). 2) Вставка операторов протоколирования (печати) промежуточных результатов (logging). Пример вставки операторов протоколирования промежуточных результатов Можно выводить промежуточные значения переменных при выполне- нии программы. Код, осуществляющий вывод, помечен светлым тоном (рис. 2.3). Этот метод относится к наиболее популярным средствам автоматизации отладки программистов прошлых десятилетий. В на- стоящее время он известен как метод внедрения «агентов» в текст отлаживаемой программы. // Метод вычисляет неотрицательную степень п числа х static public double Power(double x, int n) { double z=l; for (int i=l;n>=i;i++) { z = z*x; Console.WriteLine("i = {0} z - {l}",i,z); } return z; } Рис. 2.3. Исходный текст метода Power со вставкой оператора про- токолирования 3) Пошаговое выполнение программы (single-step running). Пример пошагового выполнения программы При пошаговом выполнении программы код выполняется строчка за строчкой. В среде Microsoft Visual Studio.NET возможны следующие ко-
манды пошагового выполнения: • Step Into — если выполняемая строчка кода содержит вызов функ- ции, процедуры или метода, то происходит вызов, и программа ос- танавливается на первой строчке вызываемой функции, процедуры или метода. • Step Over — если выполняемая строчка кода содержит вызов функ- ции, процедуры или метода, то происходит вызов и выполнение всей функции и программа останавливается на первой строчке после вы- зываемой функции. • Step Out — предназначена для выхода из функции в вызывающую функцию. Эта команда продолжит выполнение функции и остано- вит выполнение на первой строчке после вызываемой функции. Пошаговое выполнение до сих пор является мощным методом автоном- ного тестирования и отладки небольших программ. 4) Выполнение с заказанными остановками (breakpoints), анализом трасс (traces) или состояний памяти — дампов (dump). Пример выполнения программы с заказанными контрольными точками и анализом трасс и дампов • Контрольная точка (breakpoint) — точка программы, которая при ее достижении посылает отладчику сигнал. По этому сигналу либо временно приостанавливается выполнение отлаживаемой програм- мы, либо запускается программа «агент», фиксирующая состояние заранее определенных переменных или областей в данный момент. ♦ Когда выполнение в контрольной точке приостанавливается, от- лаживаемая программа переходит в режим останова (break mode). Вход в режим останова не прерывает и не заканчивает выполнение программы и позволяет анализировать состояние от- дельных переменных или структур данных. Возврат из режима break mode в режим выполнения может произойти в любой мо- мент по желанию пользователя. ♦ Когда в контрольной точке вызывается программа «агент», она тоже приостанавливает выполнение отлаживаемой программы, но только на время, необходимое для фиксации состояния выбран- ных переменных или структур данных в специальном электронном журнале — log-файле, после чего происходит автоматический возврат в режим исполнения. • Трасса — это «сохраненный путь» на управляющем графе програм- мы, т.е. зафиксированные в журнале записи о состояниях перемен- ных в заданных точках в ходе выполнения программы.
Например: на рис. 2.4 условно изображен управляющий граф некоторой программы. Трасса, проходящая через вершины 0-1-3-4-5зафиксирова- на в табл. 2.1. Строки таблицы отображают вершины управляющего графа программы, или breakpoints, в которых фиксировались текущие значения заказанных пользователем переменных. Табл. 2.1. Трасса, проходящая через вершины 0-1-3-4-5 № вершины- оператора Значение переменной х Значение переменной z Значение переменной п Значение переменной i 0 3 1 2 не зафиксировано 1 3 1 2 не зафиксировано 3 3 1 2 1 4 3 3 2 2 5 3 3 2 не зафиксировано • Дамп — область памяти, состояние которой фиксируется в конт- рольной точке в виде единого массива или нескольких связанных мас- сивов. При анализе, который осуществляется после выполнения трассы в режиме off-line, состояния дампа структурируются, и вы- деленные области или поля сравниваются с состояниями, предусмо- тренными спецификацией. Например, при моделировании поведения управляющих программ контроллеров в виде дампа фиксируются об- ласти общих и специальных регистров, или целые области оператив-
ной памяти, состояния которой определяет алгоритм управления внешней средой. 5) реверсивное (обратное) выполнение (reversible execution) Обратное выполнение программы возможно при условии сохране- ния на каждом шаге программы всех значений переменных или состоя- ний программы для соответствующей трассы. Тогда поднимаясь от ко- нечной точки трассы к любой другой, можно по шагам произвести вычис- ления состояний, двигаясь от следствия к причине, от состояний на вы- ходе преобразователя данных к состояниям на его входе. Естественно, та- кие возможности мы получаем в режиме off-line анализа при фиксации в log-файле всей истории выполнения трассы. Пример обратного выполнения для программы вычисления степени числа х В программе на рис. 2.5 фиксируются значения всех переменных после выполнения каждого оператора. // Метод вычисляет неотрицательную степень п числа х static public double PowerNonNeg(double x, int n) { double z=l; Console.WriteLine("x={0} z={l} n={2}",x,z,n); if (n>0) { Console.WriteLine("x={0} z={l} n={2}",x,z,n); for (int i=l;n>=i;i++) { z = z*x; Console.WriteLine("x:={0} z={l} n={2} i={3)",x,z,n,i); } } else Console.WriteLine("Ошибка! Степень числа n должна быть больше 0."); return z; } Рис. 2.5. Исходный код с фиксацией результатов выполнения опеа- торов
Зная структуру управляющего графа программы и имея значения всех переменных после выполнения каждого оператора, можно осуществить обратное выполнение (например, в уме), подставляя значения перемен- ных в операторы и двигаясь снизу вверх, начиная с последнего. Итак, в процессе тестирования сравнение промежуточных резуль- татов с полученными независимо эталонными результатами позволяет найти причины и место ошибки, исправить текст программы, провес- ти повторную трансляцию и настройку на выполнение и продолжить тестирование. Тестирование заканчивается, когда выполнилось или «прошло» (pass) успешно достаточное количество тестов в соответствии с выбран- ным критерием тестирования. Тестирование — это: • Процесс выполнения ПО системы или компонента в условиях анализа или записи получаемых результатов с целью проверки (оценки) некоторых свойств тестируемого объекта. (The process of operating a system or component under specified con- ditions, observing or recording the results, and making an evaluation of some aspect of the system or component [9].) • Процесс анализа пункта требований к ПО с целью фиксации раз- личий между существующим состоянием ПО и требуемым (что свидетельствует о проявлении ошибки) при экспериментальной проверке соответствующего пункта требований. (The process of analyzing a software item to detect the differences between existing and required conditions (that is, bugs) and to evaluate features of software items [[IEEE Std.610-12.1990], [9].) • Контролируемое выполнение программы на конечном множест- ве тестовых данных и анализ результатов этого выполнения для поиска ошибок [IEEE Std 829-1983]. Сквозной пример тестирования Возьмем несколько отличающуюся от рис. 2.5 программу. Для приведенной программы, вычисляющей степень числа (рис. 2.6), вос- произведем последовательность действий, необходимых для тестиро- вания.
// Метод вычисляет степень п числа х static public double Power(int x, int n) { int z=l; for (int i=l;n>=i;i++) { z = z*x; } return z; } [STAThread] static void Main(string[] args) { int x; int n; try { Console.WriteLine("Enter x:"); x=Convert.Tolnt32(Console.ReadLine()); if ((x>=0) & (x<=999)) { Console.WriteLine("Enter n:"); n=Convert.Tolnt32(Console.ReadLine()); if ((n>=l) & (n<=100)) { Console.WriteLine("The power n of x is {0}", Power(x,n)); Console.ReadLine(); } else { Console.WriteLine("Error : n must be in [1..100]"); Console.ReadLine(); } } else { Console.WriteLine("Error : x must be in [0..999]"); Console.ReadLine(); } }
catch (Exception е) { Console.WriteLine("Error : Please enter a numeric argument."); Console.ReadLine(); } Рис. 2.6. Другой пример вычисления степени числа Спецификация программы На вход программа принимает два параметра: х — число, п — степень. Результат вычисления выводится на консоль. Значения числа и степени должны быть целыми. Значения числа, возводимого в степень, должны лежать в диапазоне — [0..999]. Значения степени должны лежать в диапазоне — [1.. 100]. Если числа, подаваемые на вход, лежат за пределами указанных диапа- зонов, то должно выдаваться сообщение об ошибке. Разработка тестов Определим области эквивалентности входных параметров. Для х — числа, возводимого в степень, определим классы возмож- ных значений: 1) х< 0 (ошибочное); 2) х> 999 (ошибочное); 3)х —не число (ошибочное); 4) 0 < х < 999 (корректное). Для п — степени числа: 5) п < 1 (ошибочное); 6) п > 99 (ошибочное); 7) п — не число (ошибочное); 8) 1 < п < 100 (корректное). Анализ тестовых случаев 1. Входные значения: (х = 2, п = 3) (покрывают классы 4, 8). Ожидаемый результат: The power п of х is 8. 2. Входные значения: {(х~ -1, п — 2),(х — 1000, п = 5)} (покрыва- ют классы 1, 2). Ожидаемый результат: Error: х must be in [0..999]. 3, Входные значения: {(х = 100, п — 0),(х = 100, п = 200)} (покры-
вают классы 5,6). Ожидаемый результат: Error: п must be in [ 1.. 100]. 4. Входные значения:(х = ADS п = ASD) (покрывают классы экви- валентности 3, 7). Ожидаемый результат: Error: Please enter a numeric argument. 5. Проверка на граничные значения: 5.1 Входные значения: (х = 999 п = 1). Ожидаемый результат: The power п of х is 999. 5.2 Входные значения:х = On = 100. Ожидаемый результат: The power п ofxis 0. Выполнение тестовых случаев Запустим программу с заданными значениями аргументов. Оценка результатов выполнения программы на тестах В процессе тестирования Оракул последовательно получает элементы множества (X,Y) и соответствующие им результаты вычислений YB. В процессе тестирования производится оценка результатов выполне- ния путем сравнения получаемого результата с ожидаемым. Три фазы тестирования Реализация тестирования разделяется на три этапа: • Создание тестового набора (test suite) путем ручной разработки или автоматической генерации для конкретной среды тестирова- ния (testing environment). • Прогон программы на тестах, управляемый тестовым монитором (test monitor, test driver [IEEE Std 829-1983], [9]) с получением про- токола результатов тестирования (test log). • Оценка результатов выполнения программы на наборе тестов с целью принятия решения о продолжении или остановке тестиро- вания. Основная проблема тестирования — определение достаточности мно- жества тестов для истинности вывода о правильности реализации програм- мы, а также нахождения множества тестов, обладающего этим свойством. Простой пример Рассмотрим вопросы тестирования на примере простой программы (рис. 2.7) на языке С#. Текст этой программы и некоторых других несколько видоизменен с целью сделать иллюстрацию описываемых фа- ктов более прозрачной.
И Метод вычисляет неотрицательную степень п числа х 1 static public double Power(double x, int n){ 2 double z=l; 3 for (int i=l; 4 n>=i; 5 i++) 6 {z = z*x;} //Возврат в п.4 7 return z;} Рис. 2.7. Пример простой программы на языке С#. Рис. 2.8. Управляющий граф программы Управляющий граф программы (УГП) на рис. 2.8 отображает поток управления программы. Нумерация узлов графа совпадает с нумерацией строк программы. Узлы 1 и 2 не включаются в УГП, поскольку отобража- ют строки описаний, т.е. не содержат управляющих операторов. Управляющий граф программы Управляющий граф программы (УГП) — граф G(V,A), где V(V|,...,Vm) — множеств вершин (операторов), А(А!,...,АП) — множество дуг (управле- ний), соединяющих операторы-вершины.
Путь — последовательность вершин и дуг УГП, в которой любая ду- га выходит из вершины V, и приходит в вершину Vj, например: (3,4,7), (3,4,5,6,4,5,6), (3,4), (3,4,5,6) Ветвь — путь (V|, V2, ..., Vk), где V| — либо первый, либо условный оператор программы, Vk— либо условный оператор, либо оператор вы- хода из программы, а все остальные операторы — безусловные, напри- мер: (3,4) (4,5,6,4) (4,7). Пути, различающиеся хотя бы числом прохож- дений цикла — разные пути, поэтому число путей в программе может быть не ограничено. Ветви — линейные участки программы, их конеч- ное число. Существуют реализуемые и нереализуемые пути в программе, в нереа- лизуемые пути в обычных условиях попасть нельзя. public static float Н(float х,float у) { float Н; 1 if (х*х+у*у+2<=0) 2 Н = 17; 3 else Н = 64; 4 return Н*Н+х*х; } Рис. 2.9. Пример описания функции с реализуемыми и нереализуе- мыми путями Например, для функции рис. 2.9 путь (1,3,4) реализуем, путь (1,2,4) нереализуем в условиях нормальной работы. Но при сбоях даже нереали- зуемый путь может реализоваться. Основные проблемы тестирования • Рассмотрим два примера тестирования. Пусть программа Н(х: int, у: int) реализована в машине с 64 раз- рядным словом, тогда мощность множества тестов ||(Х,Y)||=264. Это означает, что компьютеру, работающему на частоте 1Ггц, для прогона этого набора тестов (при условии, что один тест выполняется за 100 команд) потребуется ~ ЗК лет. На рис. 2.11 приведен фрагмент схемы программы управления охва- том робота, где интервал между моментами срабатывания схвата не опре- делен. Этот тривиальный пример требует прогона бесконечного множества
последовательностей входных значений с разными интервалами срабаты- вания схвата (рис. 2.10). /) Прочитать значения датчика static public bool ReadSensor(bool Sensor) { II...чтение значения датчика Console.WriteLine("...reading sensor value"); return Sensor; } // Открыть схват static public void OpenHandO { //...открываем схват Console.WriteLine("...opening hand"); } // Закрыть схват static public void CloseHandO { II...закрываем схват Console.WriteLine("...closing hand"); } [STAThread] static void Main(string[] args) ' { while (true) { Console.WriteLine("Enter Sensor value (true/false)"); if (ReadSensor (Convert. ToBoolean (Console. ReadLineO ))) { OpenHand(); CloseHandO ; } 1 } Рис. 2.10. Фрагмент программы срабатывания схвата
Моменты срабатывания схвата Время Рис. 2.11. Тестовая последовательность сигналов датчика схвата Отсюда вывод: • Тестирование программы на всех входных значениях невозможно. • Невозможно тестирование и на всех путях. • Следовательно, надо отбирать конечный набор тестов, позволяю- щий проверить программу на основе наших интуитивных предста- влений. Требование к тестам — программа на любом из них должна останав- ливаться, т.е. не зацикливаться. Можно ли заранее гарантировать останов на любом тесте? • В теории алгоритмов доказано, что не существует общего метода для решения этого вопроса, а также вопроса, достигнет ли про- грамма на данном тесте заранее фиксированного оператора. Задача о выборе конечного набора тестов (X, Y) для проверки програм- мы в общем случае неразрешима. Поэтому для решения практических задач остается искать частные случаи решения этой задачи.
Лекция 3. Критерии выбора тестов Рассматриваются требования к идеальному критерию тестирования и классы частных критериев. Рассматриваются особенности применения стру- ктурных и функциональных критериев на базе конкретных примеров. Рас- сматриваются особенности применения методов стохастического тестирова- ния и метод оценки скорости выявления ошибок. Описывается мутационный критерий и на примере иллюстрируется техника работы с ним. Ключевые слова: идеальный критерий тестирования, достаточность, полнота, надежность, частные критерии тестирования, структурный кри- терий, функциональный критерий, стохастическое тестирование, оценка скорости выявления ошибок, плотность ошибок, интервал между ошиб- ками, мутационный критерий, мутации, мутанты. Критерии выбора тестов Требования к идеальному критерию тестирования Требования к идеальному критерию были выдвинуты в работе [11]: 1. Критерий должен быть достаточным, т.е. показывать, когда неко- торое конечное множество тестов достаточно для тестирования данной программы. 2. Критерий должен быть полным, т.е. в случае ошибки должен суще- ствовать тест из множества тестов, удовлетворяющих критерию, который раскрывает ошибку. 3. Критерий должен быть надежным, т.е. любые два множества тес- тов, удовлетворяющих ему, одновременно должны раскрывать или не рас- крывать ошибки программы. 4. Критерий должен быть легко проверяемым, например вычисляе- мым на тестах. Для нетривиальных классов программ в общем случае не существует полного и надежного критерия, зависящего от программ или спецификаций. Поэтому мы стремимся к идеальному общему критерию через реаль- ные частные. Классы критериев I. Структурные критерии используют информацию о структуре про- граммы (критерии так называемого «белого ящика»).
II. Функциональные критерии формулируются в описании требова- ний к программному изделию (критерии так называемого «черно- го ящика»), III. Критерии стохастического тестирования формулируются в тер- минах проверки наличия заданных свойств у тестируемого при- ложения, средствами проверки некоторой статистической гипо- тезы. IV. Мутационные критерии ориентированы на проверку свойств программного изделия на основе подхода Монте-Карло. Структурные критерии (класс I) Структурные критерии используют модель программы в виде «бело- го ящика», что предполагает знание исходного текста программы или спецификации программы в виде потокового графа управления. Струк- турная информация понятна и доступна разработчикам подсистем и мо- дулей приложения, поэтому данный класс критериев часто используется на этапах модульного и интеграционного тестирования (Unit testing, Integration testing). Структурные критерии базируются на основных элементах УГП, операторах, ветвях и путях: • Условие критерия тестирования команд (критерий СО) — набор тестов в совокупности должен обеспечить прохождение каждой команды не менее одного раза. Это слабый критерий, он, как пра- вило, используется в больших программных системах, где другие критерии применить невозможно. • Условие критерия тестирования ветвей (критерий С1) — набор тестов в совокупности должен обеспечить прохождение каждой ветви не менее одного раза. Это достаточно сильный и при этом экономичный критерий, поскольку множество ветвей в тестируе- мом приложении конечно и не так уж велико. Данный критерий часто используется в системах автоматизации тестирования. • Условие критерия тестирования путей (критерий С2) — набор те- стов в совокупности должен обеспечить прохождение каждого пути не менее одного раза. Если программа содержит цикл (в осо- бенности с неявно заданным числом итераций), то число итера- ций ограничивается константой (часто — 2, или числом классов выходных путей). На рис. 3.1 приведен пример простой программы. Рассмотрим усло- вия ее тестирования в соответствии со структурными критериями.
1 public void Method (ref int x) { 2 if (x>17) 3 x = 17-x; 4 if (x==-13) 5 x = 0; 6 } Рис. 3.1. Пример простой программы для тестирования по струк- турным критериям Тестовый набор из одного теста, удовлетворяет критерию команд (СО): (X,Y)={(xBX=30, хвых=0)} покрывает все операторы трассы 1-2-3-4-5-6. Тестовый набор из двух тестов, удовлетворяет критерию ветвей (С1): (Х,У)={(30,0), (17,17)} добавляет 1 тест к множеству тестов для СО и трассу 1-2-4-6. Трасса 1-2-3-4-5-6 проходит через все ветви достижи- мые в операторах if при условии true, а трасса 1 -2-4-6 — через все вет- ви, достижимые в операторах if при условии false. Тестовый набор из четырех тестов, удовлетворяет критерию путей (С2): (X,Y)={(30,0), (17,17), (-13,0), (21,-4)}. Набор условий для двух операторов if с метками 2 и 4 приведен в табл. 3.1. Табл. 3.1. Условия операторов if (30,0) (17,17) (-13,0) (21,-4) 2 if (х>17) > > 4 if (х==-13) = * = Критерий ветвей С2 проверяет программу более тщательно, чем кри- терии — С1, однако даже в случае, если он удовлетворен, нет оснований ут- верждать, что программа реализована в соответствии со спецификацией. Например, в случае, если спецификация задает условие, что |х| < 100, невыполнимость которого можно подтвердить на тесте (-177,-177). Дей- ствительно, операторы 3 и 4 на тесте (-177,-177) не изменят величину х=- 177 и результат не будет соответствовать спецификации. Структурные критерии не проверяют соответствие спецификации, если оно не отраже-
но в структуре программы. Поэтому при успешном тестировании про- граммы по критерию С2 мы можем не заметить ошибку, связанную с не- выполнением некоторых условий спецификации требований. Функциональные критерии (класс II) Функциональный критерий — важнейший для программной инду- стрии критерий тестирования. Он обеспечивает, прежде всего, контроль степени выполнения требований заказчика в программном продукте. По- скольку требования формулируются к продукту в целом, они отражают взаимодействие тестируемого приложения с окружением. При функцио- нальном тестировании преимущественно используется модель «черного ящика». Проблема функционального тестирования — это, прежде всего, трудоемкость; дело в том, что документы, фиксирующие требования к программному изделию (Software requirement specification, Functional specification и т.п.), как правило, достаточно объемны, тем не менее, соот- ветствующая проверка должна быть всеобъемлющей. Ниже приведены частные виды функциональных критериев. • Тестирование пунктов спецификации — набор тестов в совокупно- сти должен обеспечить проверку каждого тестируемого пункта не менее одного раза. Спецификация требований может содержать сотни и тысячи пунк- тов требований к программному продукту, и каждое из этих тре- бований при тестировании должно быть проверено в соответст- вии с критерием не менее чем одним тестом. • Тестирование классов входных данных — набор тестов в совокупно- сти должен обеспечить проверку представителя каждого класса входных данных не менее одного раза. При создании тестов классы входных данных сопоставляются с режимами использования тестируемого компонента или подсис- темы приложения, что заметно сокращает варианты перебора, учитываемые при разработке тестовых наборов. Следует заме- тить, что, перебирая в соответствии с критерием величины вход- ных переменных (например, различные файлы — источники входных данных), мы вынуждены применять мощные тестовые наборы. Действительно, наряду с ограничениями на величины входных данных, существуют ограничения на величины входных данных во всевозможных комбинациях, в том числе проверка ре- акций системы на появление ошибок в значениях или структурах входных данных. Учет этого многообразия — процесс трудоемкий, что создает сложности для применения критерия • Тестирование правил — набор тестов в совокупности должен обес-
печить проверку каждого правила, если входные и выходные зна- чения описываются набором правил некоторой грамматики. Следует заметить, что грамматика должна быть достаточно про- стой, чтобы трудоемкость разработки соответствующего набора тестов была реальной (вписывалась в сроки и штат специалистов, выделенных для реализации фазы тестирования). • Тестирование классов выходных данных — набор тестов в совокуп- ности должен обеспечить проверку представителя каждого вы- ходного класса, при условии, что выходные результаты заранее расклассифицированы, причем отдельные классы результатов учитывают, в том числе, ограничения на ресурсы или на время (time out). При создании тестов классы выходных данных сопоставляются с режимами использования тестируемого компонента или подсис- темы, что заметно сокращает варианты перебора, учитываемые при разработке тестовых наборов. • Тестирование функции — набор тестов в совокупности должен обеспечить проверку каждого действия, реализуемого тестируе- мым модулем, не менее одного раза. Очень популярный на практике критерий, который, однако, не обеспечивает покрытия части функциональности тестируемо- го компонента, связанной со структурными и поведенческими свойствами, описание которых не сосредоточено в отдельных функциях (т.е. описание рассредоточено по компоненту). Критерий тестирования функций отчасти объединяет особенно- сти структурных и функциональных критериев. Он базируется на модели «полупрозрачного ящика», где явно указаны не только входы и выходы тестируемого компонента, но также состав и структура используемых методов (функций, процедур) и классов. • Комбинированные критерии для программ и спецификаций — набор тестов в совокупности должен обеспечить проверку всех комби- наций непротиворечивых условий программ и спецификаций не менее одного раза. При этом все комбинации непротиворечивых условий надо подтвер- дить, а условия противоречий следует обнаружить и ликвидировать. Пример применения функциональных критериев тестирования для разработки набора тестов по критерию классов входных данных Пусть для решения задачи тестирования системы «Система управления автоматизированным комплексом хранения подшипников» (см. приложение 2,
FS) был разработан следующий фрагмент спецификации требований: 1. Произвести опрос статуса склада (вызвать функцию GetStoreStat). Добавить в журнал сообщений запись «СИСТЕМА: Запрошен ста- тус СКЛАДА». В зависимости от полученного значения произве- сти следующие действия: а. Полученный статус склада = 32. В приемную ячейку склада поступил подшипник. Система должна: • Добавить в журнал сообщений запись «СКЛАД: Статус СКЛАДА = 32». • Получить параметры поступившего подшипника с тер- минала подшипника (должна быть вызвана функция GetRollerPar). • Добавить в журнал сообщений запись «СИСТЕМА: За- прошены параметры подшипника». • В зависимости от возвращенного функцией GetRollerPar значения должны быть выполнены следую- щие действия (табл. 3.2): Табл. 3.2. Действия по результатам функции GetRollerPar Значение, возвра- щенное функцией GetRollerPar Действия системы ... ... 0 1. Добавить на первое место команду GetR - «ПОЛУЧИТЬ ИЗ ПРИЕМНИКА В ЯЧЕЙКУ» 2. Добавить в журнал сообщений запись «ТЕР- МИНАЛ ПОДШИПНИКА: 0 - параметры воз- вращены <Номер_группы>» 1 Добавить в журнал сообщений запись «ТЕР- МИНАЛ ПОДШИПНИКА: 1 — нет данных» ... ... 2. Произвести опрос терминала оси (вызвать функцию получения со- общения от терминала — GetAxlePar). В журнал сообщений долж- но быть добавлено сообщение «СИСТЕМА: Запрошены парамет- ры оси». В зависимости от возвращенного функцией GetAxlePar значения должны быть выполнены следующие действия (табл. 3.3):
Табл. 3.3. Действия по результатам функции GetAxlePar Определим классы входных данных для параметра — статус склада: 1. Статус склада = 0 (правильный). 2. Статус склада = 4 (правильный). 3. Статус склада = 16 (правильный). 4. Статус склада = 32 (правильный). 5. Статус склада = любое другое значение (ошибочный). Теперь рассмотрим тестовые случаи: 1. Тестовый случай 1 (покрывает класс 4). Состояние окружения (входные данные — X). Статус склада — 32. • • • Ожидаемая последовательность событий (выходные данные — Y): Система запрашивает статус склада (вызов функции GetStoreStat) и получает 32. • ч • 2. Тестовый случай 2 (покрывает класс 5). Состояние окружения (входные данные — X). Статус склада — 12dfga. Ожидаемая последовательность событий (выходные данные — Y): Система запрашивает статус склада (вызов функции GetStoreStat) и согласно пункту спецификации при ошибочном значении статуса склада в журнал добавляется сообщение «СКЛАД: ОШИБКА: Неоп- ределенный статус». Стохастические критерии (класс III) Стохастическое тестирование применяется при тестировании слож- ных программных комплексов — когда набор детерминированных тестов
(Х,У) имеет громадную мощность. В случаях, когда подобный набор невозможно разработать и исполнить на фазе тестирования, можно при- менить следующую методику: • Разработать программы — имитаторы случайных последователь- ностей входных сигналов {х}. • Вычислить независимым способом значения {у} для соответству- ющих входных сигналов {х} и получить тестовый набор (X,Y). • Протестировать приложение на тестовом наборе (X,Y), используя два способа контроля результатов: — Детерминированный контроль — проверка соответствия вычис- ленного значения увк{у] значению у, полученному в результате прогона теста на наборе {х} — случайной последовательности входных сигналов, сгенерированной имитатором. — Стохастический контроль — проверка соответствия множества значений {ув}, полученного в результате прогона тестов на на- боре входных значений {х}, заранее известному распределению результатов F(Y). В этом случае множество Y неизвестно (его вычисление невоз- можно), но известен закон распределения данного множества. Критерии стохастического тестирования: • Статистические методы окончания тестирования — стохастиче- ские методы принятия решений о совпадении гипотез о распре- делении случайных величин. К ним принадлежат широко извест- ные: метод Стьюдента (St), метод Хи-квадрат (х2) и т.п. • Метод оценки скорости выявления ошибок — основан на модели скорости выявления ошибок [12], согласно которой тестирование прекращается, если оцененный интервал времени между текущей ошибкой и следующей слишком велик для фазы тестирования Рис. 3.2. Зависимость скорости выявления ошибок от времени вы- явления
При формализации модели скорости выявления ошибок (рис. 3.2) использовались следующие обозначения: N — исходное число ошибок в программном комплексе перед тести- рованием; С — константа снижения скорости выявления ошибок за счет нахо- ждения очередной ошибки; tj,t2,...tn — кортеж возрастающих интервалов обнаружения последо- вательности из п ошибок; Т — время выявления п ошибок. Если допустить, что за время Т выявлено п ошибок, то справедливо соотношение (1), утверждающее, что произведение скорости выявле- ния i ошибки и времени выявления i ошибки есть 1 по определению: (1) (N—i+l)*C*tj = 1 В этом предположении справедливо соотношение (2) для п ошибок: (2) N*C*tj+(N—1 )*C*t2+...+(N-n+1 )*C*tn=n N*C*(t1+t2+...+tn) - C*2 (i-l)tj = n NCT — C*2 (i-l)ti = n Если из (1) определить tj и просуммировать от 1 до п, то придем к со- отношению (3) для времени Т выявления п ошибок: (3)£ l/(N-i+l) = TC Если из (2) выразить С, приходим к соотношению (4): (4)C = n/(NT-2(i-l)ti Наконец, подставляя С в (3), получаем окончательное соотношение (5), удобное для оценок: (5) S l/(N-i+1) = n/(N—1/Т* J (i-Oq Если оценить величину N приблизительно, используя известные ме- тоды оценки числа ошибок в программе [2],[13] или данные о плотности ошибок для проектов рассматриваемого класса из исторической базы данных проектов, и, кроме того, использовать текущие данные об интер- валах между ошибками Ц,12,...,1п, полученные на фазе тестирования, то, подставляя эти данные в (5), можно получить оценку tn+l-временного интервала, необходимого для нахождения и исправления очередной ошибки (будущей ошибки). Если tn+1 >Td — допустимого времени тестирования проекта — то те- стирование заканчиваем, в противном случае продолжаем поиск ошибок. Наблюдая последовательность интервалов ошибок t|,t2,...,tn, и вре- мя, потраченное на выявление п ошибок Т= 2^, можно прогнозировать интервал времени до следующей ошибки и уточнять в соответствии с (4) величину С. Критерий Moranda очень практичен, так как опирается на информа- цию, традиционно собираемую в процессе тестирования.
Мутационный критерий (класс IV) Постулируется, что профессиональные программисты пишут сразу почти правильные программы, отличающиеся от правильных мелкими ошибками или описками типа — перестановка местами максимальных значений индексов в описании массивов, ошибки в знаках арифметиче- ских операций, занижение или завышение границы цикла на 1 и т.п. Предлагается подход, позволяющий на основе мелких ошибок оценить общее число ошибок, оставшихся в программе. Подход базируется на следующих понятиях: Мутации — мелкие ошибки в программе. Мутанты — программы, отличающиеся друг от друга мутациями. Метод мутационного тестирования — в разрабатываемую программу Р вносят мутации, т.е. искусственно создают программы-мутанты Р1,Р2... Затем программа Р и ее мутанты тестируются на одном и том же наборе тестов (X,Y). Если на наборе (X,Y) подтверждается правильность программы Р и, кроме того, выявляются все внесенные в программы-мутанты ошибки, то набор тестов (X, Y) соответствует мутационному критерию, а тестируе- мая программа объявляется правильной. Если некоторые мутанты не выявили всех мутаций, то надо расши- рять набор тестов (Х,У) и продолжать тестирование. Пример применения мутационного критерия Тестируемая программа Р приведена на рис. 3.3. Для нее создается две программы-мутанта — Р1 и Р2. В Р1 изменено начальное значение переменной zc 1 на 2 (рис. 3.3). В Р2 изменено начальное значение переменной i с 1 на 0 и граничное зна- чение индекса цикла с п на п-1 (рис. 3.4/ При запуске тестов (X, Y = {(х=2,п=3,у=8),(х=999,п=1,у=999),(х=0,п=100,у=0} выявляются все ошибки в программах-мутантах и ошибка в основной программе, где в условии цикла вместо п стоит п-1.
// Метод вычисляет неотрицательную степень п числа х static public double PowerNonNeg(double x, int { double z=l; if (n>0) { for (int i=l;n-l>=i;i++) { z = z*x; } ) n) else Console.WriteLine("Ошибка ! Степень быть больше 0."); return z; } числа n должна Рис. 3.3. Основная программа Р Измененное начальное значение переменной z в мутанте Р1 помечено жирным шрифтом. // Метод вычисляет неотрицательную степень п числа х static public double PowerMutantl(double x, int n) { double z=2; if (n>0) r { for (int i=l;n>=i;i++) { Z = Z*X; } } else Console.WriteLine("Ошибка ! Степень числа n должна быть больше 0."); return z; } Рис. 3.4. Программа-мутант Pl Измененное начальное значение переменной i и границы цикла в мутан- те Р2 помечено жирным шрифтом.
// Метод вычисляет неотрицательную степень п числа х static public double PowerMutant2(double x, int n) { double z=l; if (n>0) { for (int i=0;n-l>=i;i++) { z = z*x; ) } else Console.WriteLine("Ошибка ! Степень числа n должна быть больше 0"); return z; } Рис. 3.5. Программа-мутант Р2
Лекция 4. Оценка оттестированности проекта: метрики и методика интегральной оценки Рассматриваются графовые модели проекта, приводятся метрики оцен- ки оттестированности проекта, приводятся примеры плоской и иерархиче- ской моделей проекта. Ключевые слова: графовая модель проекта, плоская модель, классо- вая модель, сложность тестирования, степень оттестированности проек- та, метрики оттестированности. Оценка Покрытия Программы и Проекта Тестирование программы Р по некоторому критерию С означает по- крытие множества компонентов программы Р М= {m|,...,mk} по элемен- там или по связям. T={t|,...,tn} — кортеж неизбыточных тестов ti. Тест tj неизбыточен, если существует покрытый им компонент mj из М(Р,С), не покрытый ни одним из предыдущих тестов Ц,...,^. Каждому tj соответствует неизбыточный путь р, — последовательность вершин от входа до выхода. V(P,C) — сложность тестирования Р по критерию С — измеряется max числом неизбыточных тестов, покрывающих все элементы множест- ва М(Р,С). DV(P,C,T) — остаточная сложность тестирования Р по критерию С — измеряется max числом неизбыточных тестов, покрывающих элементы множества М(Р,С), оставшиеся непокрытыми, после прогона набора тес- тов Т. Величина DV строго и монотонно убывает от V до 0. TV(P,C,T) = (V-DV)/V — оценка степени тестированности Р по кри- терию С. Критерий окончания тестирования TV(P,C,T) > L, где (0 < L < 1). L — уровень оттестированности, заданный в требованиях к программному продукту. Рис. 4.1. Метрика оттестированности приложения
Рассмотрим две модели программного обеспечения, используемые при оценке оттестированноси. Для оценки степени оттестированности часто используется УГП — управляющий граф программы. УГП многокомпонентного объекта G (рис. 4.2, рис. 4.8), содержит внутри себя два компонента G1 и G2, УГП которых раскрыты. Рис. 4.2. Плоская модель УГП компонента G В результате УГП компонента G имеет такой вид, как если бы ком- поненты G1 и G2 в его структуре специально не выделялись, а УГП ком- понентов G1 и G2 были вставлены в УГП G. Для тестирования компо-
Лекция 4 Оценка оттестированности проекта: метрики и методика интегральной оценки нента G в соответствии с критерием путей потребуется прогнать тестовый набор, покрывающий следующий набор трасс графа G (рис. 4.3): P1(G) = 1-2-3-4-5-6-7-10; P2(G) = 1-2-3-4-6-7-10; P3(G) - 1-2-11-16-18-14-15-7-10; Р4(G) = 1-2-11-16-17-14-15-7-10; P5(G) = 1-2-11-16-12-13-14-15-7-10; P6(G) = 1-2-19-20-23-22-7-10; P7(G) = 1-2-19-20-21-22-7-10; Рис. 4.3. Набор трасс, необходимых для покрытия плоской модели УГП компонента G Рис. 4.4. Иерархическая модель УГП компонента G
УГП компонента G, представленный в виде иерархической модели, приведен на рис. 4.4 и рис. 4.9. В иерархическом УГП G входящие в его состав компоненты представлены ссылками на свои УГП G1 и G2 (рис. 3.10 и рис. 4.9) Рис. 4.5. Иерархическая модель: УГП компонентов G1 и G2 Для исчерпывающего тестирования иерархической модели компо- нента G в соответствии с критерием путей требуется прогнать следующий набор трасс (рис. 4.6): P1(G) = 1-2-3-4-5-6-7-10; P2(G) = 1-2-3-4-6-7-10; P3(G) = 1-2-8-7-10; P4(G) = 1-2-9-7-10. Рис. 4.6. Набор трасс, необходимых для покрытия иерархической модели УГП компонента G Приведенный набор трасс достаточен при условии, что компоненты G1 и G2 в свою очередь исчерпывающе протестированы. Чтобы обеспе- чить выполнение этого условия в соответствии с критерием путей, надо прогнать все трассы рис. 4.7.
РИ(G1)=11-16-12-13-14-15; Р21(G2)=19-20-21-22. Р12(G1)=11-16-17-14-15. Р22(G2)=11-16-18-17-14-15 Р13(G1)=19-20-23-22. Рис. 4.7. Набор трасс иерархической модели УГП, необходимых для покрытия УГП компонентов G1 и G2 Оценка степени тестированности плоской модели определяется до- лей прогнанных трасс из набора необходимых для покрытия в соответст- вии с критерием С. (1)TV(G,C) = (V—DV)/V = £РТ;(С)/(2Р((С)), где PTj(G) - тестовый путь (tj) в графе G плоской модели равен 1, если он протестирован (про- гнан), или 0, если нет. Например, если в УГП (рис. 4.5) тесты t6 и t8, которым соответству- ют трассы Р6 и Р8, не прогнаны, то в соответствии с соотношением (1) для TV(G,C) степень тестированности будет оценена в 0.71. Оценка тестированности иерархической модели определяется на ос- нове учета оценок тестированности компонентов. Если трасса некоторо- го теста tj УГП G включает узлы, представляющие компоненты Gj],..Gjm, оценка TV степени тестированности которых известна, то оценка тести- рованности PTj(G) при реализации этой трассы определяется не 1, а ми- нимальной* из оценок TV для компонентов. Интегральная оценка определяется соотношением (2): (2) TV(G,C) = (V—DV)/V = (SPTi(G)*2(TV(Gij,C))) I QPi(G)), где PTi(G) — тестовый путь (ti) в графе G равен 1, если протестирован, или 0, если нет. В путь РТ, графа G может входить] узлов модулей Gg со своей степенью тестированности TV(Gjj,C) из которых мы берем min, что дает худшую оценку степени тестированности пути. // Пример плоской модели проекта public void G() { int TerminalStatus=O, CommandStatus=0; bool IsPresent=true, CommandFound=true; 1 Init(); 2 switch (Terminalstatus) { case 11 : Рис. 4.8. Пример программы для плоской модели (рис. 4.2)
11 16 AddCommand(); switch (CommandStatus) case 12 : 12 13 GetMessage(); ClearQueue(); break; case 17 : 17 ClearQueue() ; break; case 18 : 18 DumpQueue(); break; 14 15 J ProcessCommand () ; Commit(); break; case 3 : 3 4 AskTerminal(); if (IsPresent) г 5 t Connect(); 6 J RebuildQueue(); break; case 19 : 19 20 SearchValidCommandf); if (CommandFound) 21 t AnalyzeCommand(); } else r 23 LogError(); } 22 MoveNextCommand(); break; I 7 10 } J LogResults(); DisposeAll();
// Пример иерархической модели проекта public void Gl() ( int CommandStatus=0; AddCommand () ; switch (Commandstatus) { case 12 : GetMessage(); ClearQueue(); break; case 17 : ClearQueue(); break; case 18 : DumpQueue(); break; } Processcommand(); Commit(); } public void G2() ( " bool CommandFound=true; SearchValidCommand(); i f (CommandFound) { ' AnalyzeCommand(); } else { LogError() ; } MoveNextCommand(); } public void G() { int TerminalStatus=O; bool IsPresent=true; 1 InitO;
2 switch (Terminalstatus) { case 11 : 8 G1(); break; case 3 : 3 AskTerminal(); 4 if (IsPresent) { 5 Connect(); } 6 RebuildQueueO; break; case 19 : // Пример иерархической модели проекта - продолжение 9 G2 () ; break; } 7 LogResults(); 10 DisposeAll() ; } Рис. 4.9. Пример программы для иерархической модели (рис. 4.4) Методика интегральной оценки тестированности 1. Выбор критерия С и приемочной оценки тестированности про- граммного проекта — L. 2. Построение дерева классов проекта и построение УГП для каждо- го модуля. 3. Модульное тестирование и оценка ТУ на модульном уровне. 4. Построение УГП, интегрирующего модули в единую иерархиче- скую (классовую) модель проекта. 5. Выбор тестовых путей для проведения интеграционного или сис- темного тестирования. 6. Генерация тестов, покрывающих тестовые пути шага 5. 7. Интегральная оценка тестированности проекта с учетом оценок тестированности модулей-компонентов. 8. Повторение шагов 5-7 до достижения заданного уровня тестиро- ванности L.
Лекция 5. Модульное и интеграционное тестирование Рассматриваются особенности модульного тестирования, обсуждаются подходы к тестированию на основе потока управления, потока данных, обсу- ждаются динамические и статические методы при структурном подходе. Рас- сматривается пример модульного тестирования. Рассматривается взаимо- связь сборки модулей и методов интеграционного тестирования. Обсуждают- ся подходы монолитного, инкрементального, нисходящего и восходящего те- стирования. Рассматриваются особенности интеграционного тестирования в процедурном программировании. Ключевые слова: модульное тестирование, поток управления, поток данных, динамические методы, статические методы при структурном подходе, сборка модулей, интеграционное тестирование, монолитное те- стирование, инкрементальное тестирование, нисходящее тестирование, восходящее тестирование процедурное программирование, граф вызовов. Разновидности тестирования Модульное тестирование Модульное тестирование — это тестирование программы на уровне отдельно взятых модулей, функций или классов. Цель модульного тести- рования состоит в выявлении локализованных в модуле ошибок в реали- зации алгоритмов, а также в определении степени готовности системы к переходу на следующий уровень разработки и тестирования. Модульное тестирование проводится по принципу «белого ящика», то есть основыва- ется на знании внутренней структуры программы, и часто включает те или иные методы анализа покрытия кода. Модульное тестирование обычно подразумевает создание вокруг ка- ждого модуля определенной среды, включающей заглушки для всех ин- терфейсов тестируемого модуля. Некоторые из них могут использоваться для подачи входных значений, другие для анализа результатов, присутст- вие третьих может быть продиктовано требованиями, накладываемыми компилятором и сборщиком. На уровне модульного тестирования проще всего обнаружить дефекты, связанные с алгоритмическими ошибками и ошибками кодирования алго- ритмов, типа работы с условиями и счетчиками циклов, а также с использо-
ванием локальных переменных и ресурсов. Ошибки, связанные с неверной трактовкой данных, некорректной реализацией интерфейсов, совместимо- стью, производительностью и т.п. обычно пропускаются на уровне модуль- ного тестирования и выявляются на более поздних стадиях тестирования. Именно эффективность обнаружения тех или иных типов дефектов должна определять стратегию модульного тестирования, то есть расста- новку акцентов при определении набора входных значений. У организа- ции, занимающейся разработкой программного обеспечения, как прави- ло, имеется историческая база данных (Repository) разработок, хранящая конкретные сведения о разработке предыдущих проектов: о версиях и сборках кода (build), зафиксированных в процессе разработки продукта, о принятых решениях, допущенных просчетах, ошибках, успехах и т.п. Проведя анализ характеристик прежних проектов, подобных заказанному организации, можно предохранить новую разработку от старых ошибок, например, определив типы дефектов, поиск которых наиболее эффекти- вен на различных этапах тестирования. В данном случае анализируется этап модульного тестирования. Если анализ не дал нужной информации, например, в случае проектов, в кото- рых соответствующие данные не собирались, то основным правилом ста- новится поиск локальных дефектов, у которых код, ресурсы и информа- ция, вовлеченные в дефект, характерны именно для данного модуля. В этом случае на модульном уровне ошибки, связанные, например, с не- верным порядком или форматом параметров модуля, могут быть пропу- щены, поскольку они вовлекают информацию, затрагивающую другие модули (а именно, спецификацию интерфейса), в то время как ошибки в алгоритме обработки параметров довольно легко обнаруживаются. Являясь по способу исполнения структурным тестированием или те- стированием «белого ящика», модульное тестирование характеризуется степенью, в которой тесты выполняют или покрывают логику программы (исходный текст). Тесты, связанные со структурным тестированием, стро- ятся по следующим принципам: • На основе анализа потока управления. В этом случае элементы, которые должны быть покрыты при прохождении тестов, опреде- ляются на основе структурных критериев тестирования СО, С1, С2. К ним относятся вершины, дуги, пути управляющего графа программы (УГП), условия, комбинации условий и т. п. • На основе анализа потока данных, когда элементы, которые должны быть покрыты, определяются на основе потока данных, т. е. информационного графа программы. Тестирование на основе потока управления. Особенности использования структурных критериев тестирования СО, Cl, С2 были рассмотрены в разде- ле 2. К ним следует добавить критерий покрытия условий, заключающийся в
покрытии всех логических (булевских) условий в программе. Критерии по- крытия решений (ветвей — С1) и условий не заменяют друг друга, поэтому на практике используется комбинированный критерий покрытия условий/ре- щсний, совмещающий требования по покрытию и решений, и условий. К популярным критериям относятся критерий покрытия функций Программы, согласно которому каждая функция программы должна быть вы »вана хотя бы один раз, и критерий покрытия вызовов, согласно кото- рому каждый вызов каждой функции в программе должен быть осущест- йлсн хотя бы один раз. Критерий покрытия вызовов известен также как Критерий покрытия пар вызовов (call pair coverage). Тестирование на основе потока данных. Этот вид тестирования напра- влен на выявление ссылок на неинициализированные переменные и из- быточные присваивания (аномалии потока данных). Как основа для стра- 1сгии тестирования поток данных впервые был описан в [14]. Предложен- ная там стратегия требовала тестирования всех взаимосвязей, включаю- щих в себя ссылку (использование) и определение переменной, на кото- рую указывает ссылка (т. е. требуется покрытие дуг информационного графа программы). Недостаток стратегии в том, что она не включает кри- терий С1 и не гарантирует покрытия решений. Стратегия требуемых пар [15] также тестирует упомянутые взаимо- связи. Использование переменной в предикате дублируется в соответст- вии с числом выходов решения, и каждая из таких требуемых взаимосвя- зи должна быть протестирована. К популярным критериям принадлежит Критерий СР, заключающийся в покрытии всех таких пар дуг v и w, что из дуги v достижима дуга w, поскольку именно на дуге может произойти по- 1Сря значения переменной, которая в дальнейшем уже не должна исполь- зоваться. Для «покрытия» еще одного популярного критерия Cdu доста- точно тестировать пары (вершина, дуга), поскольку определение пере- менной происходит в вершине УГП, а ее использование — на дугах, исхо- дящих из решений, или в вычислительных вершинах. Методы проектирования тестовых путей для достижения заданной степени тестированности в структурном тестировании. Процесс построе- ния набора тестов при структурном тестировании принято делить на три фазы: • конструирование УГП; • выбор тестовых путей; • генерация тестов, соответствующих тестовым путям. Первая фаза соответствует статическому анализу программы, задача которого состоит в получении графа программы и зависящего от него и от критерия тестирования множества элементов, которые необходимо по- крыть тестами. На третьей фазе по известным путям тестирования осуществляется
поиск подходящих тестов, реализующих прохождение этих путей. Вторая фаза обеспечивает выбор тестовых путей. Выделяют три под- хода к построению тестовых путей: • статические методы; • динамические методы; • методы реализуемых путей. Статические методы. Самое простое и легко реализуемое решение — построение каждого пути посредством постепенного его удлинения за счет добавления дуг, пока не будет достигнута выходная вершина управляюще- го графа программы. Эта идея может быть усилена в так называемых адап- тивных методах, которые каждый раз добавляют только один тестовый путь (входной тест), используя предыдущие пути (тесты) как руководство для выбора последующих путей в соответствии с некоторой стратегией. Чаще всего адаптивные стратегии применяются по отношению к критерию С1. Основной недостаток статических методов заключается в том, что не учи- тывается возможная нереализуемость построенных путей тестирования. Динамические методы. Такие методы предполагают построение пол- ной системы тестов, удовлетворяющих заданному критерию, путем одно- временного решения задачи построения покрывающего множества путей и тестовых данных. При этом можно автоматически учитывать реализуе- мость или нереализуемость ранее рассмотренных путей или их частей. Основной идеей динамических методов является подсоединение к на- чальным реализуемым отрезкам путей дальнейших их частей так, чтобы: 1) не терять при этом реализуемости вновь полученных путей; 2) покрыть требуемые элементы структуры программы. Методы реализуемых путей. Данная методика [16] заключается в вы- делении из множества путей подмножества всех реализуемых путей. Пос- ле чего покрывающее множество путей строится из полученного подмно- жества реализуемых путей. Достоинство статических методов состоит в сравнительно неболь- шом количестве необходимых ресурсов, как при использовании, так и при разработке. Однако их реализация может содержать непредсказуе- мый процент брака (нереализуемых путей). Кроме того, в этих системах переход от покрывающего множества путей к полной системе тестов пользователь должен осуществить вручную, а эта работа достаточно тру- доемкая. Динамические методы требуют значительно больших ресурсов как при разработке, так и при эксплуатации, однако увеличение затрат происходит, в основном, за счет разработки и эксплуатации аппарата оп- ределения реализуемости пути (символический интерпретатор, решатель неравенств). Достоинство этих методов заключается в том, что их продук- ция имеет некоторый качественный уровень — реализуемость путей. Ме- тоды реализуемых путей дают самый лучший результат.
Пример модульного тестирования Предлагается протестировать класс TCommand, который реализует команду для склада. Этот класс содержит единственный метод TCommand. GetFullName (), спецификация которого описана (Практи- кум, приложение 2 (HLD)) следующим образом: Операция GetFullName () возвращает полное имя команды, со- ответствующее ее допустимому коду, указанному в поле NameCommand. В противном случает возвращается сообщение «ОШИБКА: Неверный код команды». Операция может быть применена в любой момент. Разработаем спецификацию тестового случая для тестирования мето- да GetFullName на основе приведенной спецификации класса (табл. 5.1): Табл. 5.1. Спецификация теста Название класса: TCommand Название тестового случая: TCommandTestl Описание тестового случая: Тест проверяет правильность работы метода GetFullName — получения полного названия команды на ос- нове кода команды. В тесте подаются следующие значения кодов команд (входные значения): -1, 1, 2, 4, 6, 20, (причем -1 — запре- щенное значение) Начальные условия: Нет Ожидаемый результат: Перечисленным входным значениям должны соответствовать сле- дующие выходные: Коду команды -1 должно соответствовать сообщение «ОШИБКА: Неверный код команды» Коду команды 1 должно соответствовать полное название коман- ды «ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ» Коду команды 2 должно соответствовать полное название коман- ды «ОТПРАВИТЬ ИЗ ЯЧЕЙКИ В ВЫХОДНУЮ ЯЧЕЙКУ» Коду команды 4 должно соответствовать полное название коман- ды «ПОЛОЖИТЬ В РЕЗЕРВ» Коду команды 6 должно соответствовать полное название коман- ды «ПРОИЗВЕСТИ ЗАНУЛЕНИЕ» Коду команды 20 должно соответствовать полное название коман- ды «ЗАВЕРШЕНИЕ КОМАНД ВЫДАЧИ»
Для тестирования метода класса TCommand. GetFullName () был создан тестовый драйвер — класс TCommandTester. Класс TConmandTester со- держит метод TCommandTestl (), в котором реализована вся функцио- нальность теста. В данном случае для покрытия спецификации доста- точно перебрать следующие значения кодов команд: -1, 1, 2, 4, 6, 20, (-1 — запрещенное значение) и получить соответствующее им полное на- звание команды с помощью метода GetFullName () (рис. 5.1). Пары зна- чений (X, Ye) при исполнении теста заносятся в log-файл для последую- щей проверки на соответствие спецификации. После завершения теста следует просмотреть журнал теста, чтобы сравнить полученные результаты с ожидаемыми, заданными в специ- фикации тестового случая TCommandTestl (рис. 5.2). class TCommandTester:Tester // Тестовый драйвер TCommand OUT; public TCommandTester() { OUT=new TCommand(); Run(); } private void Run() { TCommandTestl() ; } private void TCommandTestl() int[] commands = {-1, 1, 2, 4, 6, 20}; for(int i=0;i<=5;i++) { OUT.NameCommand=commands[i]; LogMessage (commands [i] .ToStringO+" : "+OUT.GetFullName()); } Рис. 5.1. Тестовый драйвер
-1: ОШИБКА: Неверный код команды 1 : ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ 2 : ОТПРАВИТЬ ИЗ ЯЧЕЙКИ В ВЫХОДНУЮ ЯЧЕЙКУ 4 : ПОЛОЖИТЬ В РЕЗЕРВ 6: ПРОИЗВЕСТИ ЗАНУЛЕНИЕ 20 : ЗАВЕРШЕНИЕ КОМАНД ВЫДАЧИ Рис. 5.2. Спецификация классов тестовых случаев Интеграционное тестирование Интеграционное тестирование — это тестирование части системы, состоящей из двух и более модулей. Основная задача интеграционного те- стирования — поиск дефектов, связанных с ошибками в реализации и ин- терпретации интерфейсного взаимодействия между модулями. С технологической точки зрения интеграционное тестирование яв- ляется количественным развитием модульного, поскольку так же, как и модульное тестирование, оперирует интерфейсами модулей и подсистем и требует создания тестового окружения, включая заглушки (Stub} на ме- сте отсутствующих модулей. Основная разница между модульным и инте- грационным тестированием состоит в целях, то есть в типах обнаружива- емых дефектов, которые, в свою очередь, определяют стратегию выбора входных данных и методов анализа. В частности, на уровне интеграцион- ного тестирования часто применяются методы, связанные с покрытием интерфейсов, например, вызовов функций или методов, или анализ ис- пользования интерфейсных объектов, таких как глобальные ресурсы, средства коммуникаций, предоставляемых операционной системой. Рис. 5.3. Пример структуры комплекса программ
На рис. 4.3 приведена структура комплекса программ К, состояще- го из оттестированных на этапе модульного тестирования модулей М(, М2, М]|, М]2, М21, М22. Задача, решаемая методом интеграционного те- стирования, — тестирование межмодульных связей, реализующихся при исполнении программного обеспечения комплекса К. Интеграционное тестирование использует модель «белого ящика» на модульном уровне. Поскольку тестировщику текст программы известен с детальностью до вызова всех модулей, входящих в тестируемый комплекс, применение структурных критериев на данном этапе возможно и оправдано. Интеграционное тестирование применяется на этапе сборки мо- дульно оттестированных модулей в единый комплекс. Известны два ме- тода сборки модулей: • Монолитный, характеризующийся одновременным объединени- ем всех модулей в тестируемый комплекс. • Инкрементальный, характеризующийся пошаговым (помодуль- ным) наращиванием комплекса программ с пошаговым тестиро- ванием собираемого комплекса. В инкрементальном методе вы- деляют две стратегии добавления модулей: — «Сверху вниз» и соответствующее ему восходящее тестирование. — «Снизу вверх» и соответственно нисходящее тестирование. Особенности монолитного тестирования заключаются в следующем: для замены неразработанных к моменту тестирования модулей, кроме са- мого верхнего (К на рис. 5.3), необходимо дополнительно разрабатывать драйверы {test driver) и/или заглушки (stub) [9], замещающие отсутствую- щие на момент сеанса тестирования модули нижних уровней. Сравнение монолитного и интегрального подхода дает следующее: • Монолитное тестирование требует больших трудозатрат, связан- ных с дополнительной разработкой драйверов и заглушек и со сложностью идентификации ошибок, проявляющихся в про- странстве собранного кода. • Пошаговое тестирование связано с меньшей трудоемкостью идентификации ошибок за счет постепенного наращивания объ- ема тестируемого кода и соответственно локализации добавлен- ной области тестируемого кода. • Монолитное тестирование предоставляет большие возможности рас- параллеливания работ, особенно на начальной фазе тестирования. Особенности нисходящего тестирования заключаются в следующем: организация среды для исполняемой очередности вызовов оттестирован- ными модулями тестируемых модулей, постоянная разработка и исполь- зование заглушек, организация приоритетного тестирования модулей, содержащих операции обмена с окружением, или модулей, критичных для тестируемого алгоритма.
Например, порядок тестирования комплекса К (рис. 5.3) при нисхо- дящем тестировании может быть таким, как показано на рис. 5.4, где тес- товый набор, разработанный для модуля Мь обозначен как XY, = (X, Y)j K->XYK Mi^XYj Mir>XYn m2->xy2 m22->xy22 M2r>XY2i M12->XY12 Рис. 5.4. Возможный порядок тестов при нисходящем тестировании Недостатки нисходящего тестирования: • Проблема разработки достаточно «интеллектуальных» заглушек, т.е. заглушек, способных к использованию при моделировании различных режимов работы комплекса, необходимых для тести- рования. • Сложность организации и разработки среды для реализации ис- полнения модулей в нужной последовательности. • Параллельная разработка модулей верхних и нижних уровней приводит к не всегда эффективной реализации модулей из-за под- стройки (специализации) еще не тестированных модулей нижних уровней к уже оттестированным модулям верхних уровней. Особенности восходящего тестирования в организации порядка сбор- ки и перехода к тестированию модулей, соответствующему порядку их ре- ализации. Например, порядок тестирования комплекса К (рис. 5.3) при нисхо- дящем тестировании может быть следующим (рис. 5.5). Мц->ХУП М12->ХУ12 МГ>ХУ! М21->ХУ21 М2(М2 j, Stub(M22))->XY2 K(Mj, М2(М2Ъ Stub(M22)) ->XYK m22->xy22 m2->xy2 K->XYK Рис. 5.5. Возможный порядок тестов при восходящем тестировании
Недостатки восходящего тестирования: • Запаздывание проверки концептуальных особенностей тестиру- емого комплекса. • Необходимость в разработке и использовании драйверов. Особенности интеграционного тестирования для процедурного программирования Процесс построения набора тестов при структурном тестировании определяется принципом, на котором основывается конструирование Графа Модели Программы (ГМГГ). От этого зависит множество тестовых путей и генерация тестов, соответствующих тестовым путям. Первым подходом к разработке программного обеспечения является процедурное (модульное) программирование. Традиционное процедур- ное программирование предполагает написание исходного кода в импе- ративном (повелительном) стиле, предписывающем определенную пос- ледовательность выполнения команд, а также описание программного проекта с помощью функциональной декомпозиции. Такие языки, как Pascal и С, являются императивными. В них порядок исходных строк ко- да определяет порядок передачи управления, включая последовательное исполнение, выбор условий и повторное исполнение участков програм- мы. Каждый модуль имеет несколько точек входа (при строгом написа- нии кода — одну) и несколько точек выхода (при строгом написании ко- да — одну). Сложные программные проекты имеют модульно-иерархиче- ское построение [5], и тестирование модулей является начальным шагом процесса тестирования ПО. Построение графовой модели модуля являет- ся тривиальной задачей, а тестирование практически всегда проводится по критерию покрытия ветвей С1, т.е. каждая дуга и каждая вершина гра- фа модуля должны содержаться, по крайней мере, в одном из путей тес- тирования. Таким образом, М(Р,С1) = EUNy, где Е — множество дуг, a Ny — входные вершины ГМП. Сложность тестирования модуля по критерию С1 выражается уточ- ненной формулой для оценки топологической сложности МакКейба [10]: V(P,C1) = q + kjn , где q — число бинарных выборов для условий ветв- ления, a kin — число входов графа. Для интеграционного тестирования наиболее существенным являет- ся рассмотрение модели программы, построенной с использованием диа- грамм потоков управления. Контролируются также связи через данные, подготавливаемые и используемые другими группами программ при вза- имодействии с тестируемой группой. Каждая переменная межмодульного интерфейса проверяется на тождественность описаний во взаимодейст-
вующих модулях, а также на соогвс1сгние исходным программным спе- цификациям. Состав и структура информационных связей реализован- ной группы модулей проверяются на соотносите спецификации требо- ваний этой группы. Все реализованные связи должны bi.ni. yciaiioiineiibi. упорядочены и обобщены. При сборке модулей в единый программный комплекс iiomiuihcicm два варианта построения графовой модели проекта: • плоская или иерархическая модель проекта (например, рис. 4.2, рис. 4.4); • граф вызовов. Если программа Р состоит из р модулей, то при интеграции модулей в комплекс фактически получается громоздкая плоская (рис. 4.2) или бо- лее простая — иерархическая (рис. 4.4) — модель программного проекта. В качестве критерия тестирования на интеграционном уровне обычно ис- пользуется критерий покрытия ветвей С1. Введем также следующие обо- значения: п — число узлов в графе; е — число дуг в графе; q — число бинарных выборов из условий ветвления в графе; kjn — число входов в граф; kout —- число выходов из графов; kext — число точек входа, которые могут быть вызваны извне. Тогда сложность интеграционного тестирования всей программы Р по критерию С1 может быть выражена формулой [17]: V(P,C1) =2У(Моф, Cl) - kin +kext = е - n - kext + kout = = q + k€Xt, (VModj e P) Однако при подобном подходе к построению ГМП разработчик тесто- вого набора неизбежно сталкивается с неприемлемо высокой сложностью тестирования V(P,C) для проектов среднего и большого объема (размером в 105 — 107 строк) [18], что следует из роста топологической сложности управ- ляющего графа по МакКейбу. Таким образом, используя плоскую или ие- рархическую модель, трудно дать оценку тестированное™ TV(P,C,T) для всего проекта и оценку зависимости тестированное™ проекта от тестиро- ванное™ отдельного модуля TV(Modj,C), включенного в этот проект. Рассмотрим вторую модель сборки модулей в процедурном програм- мировании — граф вызовов. В этой модели в случае интеграционного тести- рования учитываются только вызовы модулей в программе. Поэтому из множества M(Modj,C) тестируемых элементов можно исключить те элемен- ты, которые не подвержены влиянию интеграции, т. е. узлы и дуги, не соеди- ненные с вызовами модулей: M(Modj,C) ~ Е' U Njn, где Е' = {(п,, n,) G Е | П; или rij содержит вызовы модулей}, т.е. Е' — подмножество ребер графа мо-
дуля, a Nin — «входные» узлы графа [17]. Эта модификация ГМП приво- дит к получению нового графа — графа вызовов, каждый узел в этом гра- фе представляет модуль (процедуру), а каждая дуга — вызов модуля (про- цедуры). Для процедурного программирования подобный шаг упрощает графовую модель программного проекта до приемлемого уровня сложно- сти. Таким образом, может быть определена цикломатическая сложность упрощенного графа модуля Mod, как V’(Modi,C), а громоздкая формула, выражающая сложность интеграционного тестирования программного проекта, принимает следующий вид [19]: V(P,CT) = eV(Modb С Г) - kin +kext Так, для программы, ГМП которой приведена на рис. 3.7, для получе- ния графа вызовов из иерархической модели проекта должны быть ис- ключены все дуги, кроме: 1. дуги 1-2, содержащей входной узел 1 графа G; 2. дуг 2-8, 8-7, 7-10, содержащих вызов модуля G1; 3. дуг 2-9, 9-7, 7-10, содержащих вызов модуля G2. В результате граф вызовов примет вид, показанный на рис. 4.4, а слож- ность данного графа по критерию С Г равна: V’(G,C1') = q + Kext =1+1=2. V^ModpC’) также называется в литературе сложностью модульного дизайна (complexity of module design) [19]. Сумма сложностей модульного дизайна для всех модулей по крите- рию С1 или сумма их аналогов для других критериев тестирования, ис- ключая значения модулей самого нижнего уровня, дает сложность инте- грационного тестирования для процедурного программирования [20].
Лекция 6. Интеграционное тестирование и его особенности для объектно-ориентированного программирования Рассматривается модель объектно-ориентированной программы, ис- пользующая понятие P-путей и ММ-путей. Приводятся оценки сложности тестирования и методика тестирования объектно-ориентированной програм- мы. Рассматривается пример интеграционного тестирования. Ключевые слова: объектно-ориентированная программа, P-пути и ММ-пути, оценки сложности тестирования, методика тестирования объ- ектно-ориентированной программы, классовая модель объектно-ориен- тированной программы. Особенности интеграционного тестирования для объектно-ориентированного программирования Программный проект, написанный в соответствии с объектно-ори- ентированным подходом, будет иметь ГМП, существенно отличающийся от ГМП традиционной «процедурной» программы. Сама разработка про- екта строится по другому принципу — от определения классов, использу- емых в программе, построения дерева классов к реализации кода проек- та. При правильном использовании классов, точно отражающих при- кладную область приложения, этот метод дает более короткие, понятные и легко контролируемые программы. Объектно-ориентированное программное обеспечение является со- бытийно управляемым. Передача управления внутри программы осуще- ствляется не только путем явного указания последовательности обраще- ний одних функций программы к другим, но и путем генерации сообще- ний различным объектам, разбора сообщений соответствующим обработ- чиком и передача их объектам, для которых данные сообщения предна- значены. Рассмотренная ГМП в данном случае становится непримени- мой. Эта модель, как минимум, требует адаптации к требованиям, вводи- мым объектно-ориентированным подходом к написанию программного обеспечения. При этом происходит переход от модели, описывающей структуру программы, к модели, описывающей поведение программы, что для тестирования можно классифицировать как положительное свой- ство данного перехода. Отрицательным аспектом совершаемого перехода для применения рассмотренных ранее моделей является потеря заданных в явном виде связей между модулями программы.
Перед тем как приступить к описанию графовой модели объектно- ориентированной программы, остановимся отдельно на одном сущест- венном аспекте разработки программного обеспечения на языке объект- но-ориентированного программирования (ООП), например, C++ или С#. Разработка программного обеспечения высокого качества для MS Windows или любой другой операционной системы, использующей стан- дарт «look and feel», с применением только вновь созданных классов пра- ктически невозможна. Программист должен будет затратить массу време- ни на решение стандартных задач по созданию пользовательского интер- фейса. Чтобы избежать работы над давно решенными вопросами, во всех современных компиляторах предусмотрены специальные библиотеки классов. Такие библиотеки включают в себя практически весь программ- ный интерфейс операционной системы и позволяют задействовать при программировании средства более высокого уровня, чем просто вызовы функций. Базовые конструкции и классы могут быть переиспользованы при разработке нового программного проекта. За счет этого значительно сокращается время разработки приложений. В качестве примера подоб- ной системы можно привести библиотеку Microsoft Foundation Class для компилятора MS Visual C++ [21]. Работа по тестированию приложения не должна включать в себя проверку работоспособности элементов библиотек, ставших фактически промышленным стандартом для разработки программного обеспечения, а только проверку кода, написанного непосредственно разработчиком программного проекта. Тестирование объектно-ориентированной про- граммы должно включать те же уровни, что и тестирование процедурной программы — модульное, интеграционное и системное. Внутри класса отдельно взятые методы имеют императивный характер исполнения. Все языки ООП возвращают контроль вызывающему объекту, когда сообще- ние обработано. Поэтому каждый метод (функция — член класса) должен пройти традиционное модульное тестирование по выбранному критерию С (как правило, С1). В соответствии с введенными выше обозначениями, назовем метод Modj, а сложность тестирования — V(ModpC). Все резуль- таты, полученные в пункте 4.2.1 для тестирования модулей, безусловно, подходят для тестирования методов классов. Каждый класс должен быть рассмотрен и как субъект интеграционного тестирования. Интеграция для всех методов класса проводится с использованием инкрементальной стратегии снизу вверх. При этом мы можем переиспользовать тесты для классов-родителей тестируемого класса [22], что следует из принципа на- следования — от базовых классов, не имеющих родителей, к самым верх- ним уровням классов.
Графовая модель класса, как и объектно-ориентированной про- граммы, на интеграционном уровне в качестве узлов использует методы. Дуги данной ГМП (вызовы методов) могут быть образованы двумя спо- собами: • Прямым вызовом одного метода из кода другого, в случае, если вызываемый метод виден (не закрыт для доступа средствами язы- ка программирования) из класса, содержащего вызывающий ме- тод, присвоим такой конструкции название P-путь (P-path, Procedure path, процедурный путь). • Обработкой сообщения, когда явного вызова метода нет, но в ре- зультате работы «вызывающего» метода порождается сообщение, которое должно быть обработано «вызываемым» методом. Для второго случая «вызываемый» метод может породить другое со- общение, что приводит к возникновению цепочки исполнения последо- вательности методов, связанных сообщениями. Подобная цепочка носит название ММ-путъ (ММ-path, Metod/Message path, путь метод/сообщение) [23]. ММ-путь заканчивается, когда достигается метод, который при от- работке не вырабатывает новых сообщений (т. е. вырабатывает «сообще- ние покоя»). Пример ММ-путей приведен на рис. 5.5. Данная конструкция от- ражает событийно управляемую природу объектно-ориентированного программирования и может быть взята в качестве основы для построе- ния графовой модели класса или объектно-ориентированной програм- мы в целом. На рис. 5.5 можно выделить четыре ММ-пути (1-4) и один Р-путь (5): 1. msg а -* метод 3 -» msg 3 -* метод 4 -» msg d 2. msg b -* метод 1 -» msg 1 -* метод 4 -* msg d 3. msg b -* метод 1 -* msg 2 -* метод 5 4. msg c -» метод 2 5. call -» метод 5 Здесь класс изображен как объединенное множество методов. Введем следующие обозначения: Kmsg — число методов класса, обрабатывающих различные сообще- ния; Кеп1 — число методов класса, которые не закрыты от прямого вызо- ва из других классов программы.
Рис. 6.1. Пример ММ-путей и P-путей в графовой модели класса Если рассматривать класс как программу Р, то можно выделить сле- дующие отличия от программы, построенной по процедурному принципу: • Значение Kext (число точек входа, которые могут быть вызваны извне) определяется как сумма методов — обработчиков сообще- ний Kmsg (например, в MS Visual C++ обозначаются зарезервиро- ванным словом afx msg и используются для работы с картой со- общений класса) и тех методов, которые могут быть вызваны из других классов программы Кет. Это определяется самим разра- ботчиком путем разграничения доступа к методам класса (с помо- щью ключевых слов разграничения доступа public, private, protect) при написании методов, а также назначении дружественных (friend) функций и дружественных классов. Таким образом, Kext = Kmsg + Kem, и имеет новый по сравнению с процедурным про- граммированием физический смысл. • Принцип соединения узлов в ГМП, отражающий два возможных типа вызовов методов класса (через ММ-пути и P-пути), что при- водит к новому наполнению для множества М требуемых элемен- тов. • Методы (модули) непрозрачны для внешних объектов, что влечет за собой неприменимость механизма упрощения графа модуля, используемого для получения графа вызовов в процедурном про- граммировании. С учетом приведенных замечаний, информационные связи между модулями программного проекта получают новый физический смысл, а формула оценки сложности интеграционного тестирования класса Cis принимает вид: V(Cls, C) = f(Kmsg,Kem)
J1.J.— —SS , ________ " ———- .1..— — ......... ...... В ходе интеграционного тестирования должны быть проверены все возможные внешние вызовы методов класса, как непосредственные об- ращения, так и вызовы, инициированные получением сообщений. Значение числа ММ-путей зависит от схемы обработки сообщений данным классом, что должно быть определено в спецификации класса. Например, для класса, изображенного на рис. 5.5, сложность интеграци- онного тестирования V(Cls,C)=5 (множество неизбыточных тестов Т для класса составляют 4 ММ-пути плюс внешний вызов метода 5, т. е. Р-путь). Данные — члены класса (данные, описанные в самом классе, и унас- ледованные от классов-родителей видимые извне данные) рассматрива- ются как «глобальные переменные», они должны быть протестированы отдельно на основе принципов тестирования потоков данных. Когда класс программы Р протестирован, объект данного кл'асса мо- жет быть включен в общий граф G программного проекта, содержащий все ММ-пути и все вызовы методов классов и процедур, возможные в программе (рис. 5.6): Программа Р, содержащая п классов, имеет сложность интеграцион- ного тестирования классов: V(P, С) = MV (Cis,, С) Формальным представлением описанного выше подхода к тестиро- ванию программного проекта служит классовая модель программного проекта, состоящая из дерева классов проекта (рис. 6.3) и модели каждо- го класса, входящего в программный проект (рис. 6.4). Рис. 6.2. Пример включения объекта в модель программного проек- та, построенного с использованием ММ-путей и Р-путей
Таким образом и определяется классовая модель проекта для тести- рования объектно-ориентированной программы. Как будет показано в дальнейшем, она поддерживает итерационный инкрементальный про- цесс разработки программного обеспечения. Рис. 6.4. Модель класса, входящего в программный проект
Методика проведения тестирования программы, представленной в виде классовой модели программного проекта, включает в себя несколь- ко этапов, соответствующих уровням тестирования (рис. 6.5): 1. На первом уровне проводится тестирование методов каждого класса программы, что соответствует этапу модульного тестиро- вания. 2. На втором уровне тестируются методы класса, которые образуют контекст интеграционного тестирования каждого класса. 3. На третьем уровне протестированный класс включается в общий контекст (дерево классов) программного проекта. Здесь становит- ся возможным отслеживать реакцию программы на внешние со- бытия. Второй и третий уровни рассматриваемой модели соответствуют этапу интеграционного тестирования. Для третьего уровня важным оказывается понятие атомарной сис- темной функции (АСФ) [23]. АСФ — это множество, состоящее из внешнего события на входе системы, реакции системы на это событие в виде одного или более ММ-путей и внешнего события на выходе си- стемы. В общем случае внешнее выходное событие может быть нуле- вым, т. е. неаккуратно написанное программное обеспечение может не обеспечивать внешней реакции на действия пользователя. АСФ, состо- ящая из входного внешнего события, одного ММ-пути и выходного внешнего события, может быть взята в качестве модели для нити (thread). Тестирование подобной АСФ в рамках классовой модели ГМП реализуется довольно сложно, так как хотя динамическое взаимодейст- вие нитей (потоков) в процессе исполнения естественно фиксируется в log-файлах, запоминающих результаты трассировки исполнения про- грамм, оно же достаточно сложно отображается на классовой ГМП. Причина в том, что классовая модель ориентирована на отображение статических характеристик проекта, а в данном случае требуется ото- бражение поведенческих характеристик. Как правило, тестирование взаимодействия нитей в ходе исполнения программного комплекса вы- носится на уровень системного тестирования и использует другие более приспособленные для описания поведения модели. Например, описа- ние поведения программного комплекса средствами языков специфи- каций MSC, SDL, UML. Явный учет границ между интеграционным и системным уровнями тестирования дает преимущество при планировании работ на фазе тести- рования, а возможность сочетать различные методы и критерии тестиро- вания в ходе работы над программным проектом дает наилучшие резуль- таты [24].
Объектно-ориентированный подход, ставший в настоящее время неявным стандартом разработки программных комплексов, позволяет широко использовать иерархическую модель программного проекта. Приведенная на рис. 6.5 схема иллюстрирует способ применения. Каж- дый класс рассматривается как объект модульного и интеграционного те- стирования. Сначала каждый метод класса тестируется как модуль по вы- бранному критерию С. Затем класс становится объектом интеграционно- го тестирования. Далее осуществляется интеграция всех методов всех классов в единую структуру — классовую модель проекта, где в общую ГМП протестированные модули входят в виде узлов (интерфейсов вызо- ва) без учета их внутренней структуры, а их детальные описания образуют контекст всего программного проекта. 1-й уровень 2-й уровень 3-й уровень модульное тестирование интеграционное тестирование метод 1 метод 2 класс 1 класс 2 мртоп М w- класс N Рчтрограмма Рис. 6.5. Уровни тестирования классовой модели программного проекта Сама технология объектно-ориентированного программирования (одним из определяющих принципов которой является инкапсуляция с возможностью ограничения доступа к данным и методам — членам клас- са) позволяет применить подобную трактовку вхождения модулей в об- щую ГМП. При этом тесты для отдельно рассмотренных классов переис- пользуются, входя в общий набор тестов для программы Р. Пример интеграционного тестирования Продемонстрируем тестирование взаимодействий на примере взаимо- действия класса TCommandQueue и класса TCommand, а также, как и при модульном тестировании, разработаем спецификацию тестового случая (табл. 6.1):
Табл. 6.1. Спецификация тестового случая для интеграционного те- стирования Названия взаимодействующих классов: TCommandQueue, TCommand Название теста: TCommandQueueTest1 Описание теста: тест проверяет возможность создания объекта типа TCommand и добавления его в очередь при вызове метода AddCommand Начальные условия: очередь команд пуста Ожидаемый результат: в очередь будет добавлена одна команда На основе этой спецификации разработан тестовый драйвер (рис. 6.6) — класс TCommandQueueTester, который наследуется от класса Tester. Класс содержит: — конструктор, в котором создаются объекты классов TStore, TTerminalBearing и объект типа TCommandQueue public TCommandQueueTester() { ТВ = new TTerminalBearing(); S = new TStore(); CommandQueue=new TCommandQueue(S,ТВ); S.CommandQueue=CommandQueue; Рис. 6.6. Объект типа TCommandQueue — Методы, реализующие тесты. Каждый тест реализован в отдель- ном методе. — Метод Run, в котором вызываются методы тестов. — Метод dump, который сохраняет в log-файле теста информацию обо всех командах, находящихся в очереди в формате — номер позиции в очереди: полное название команды. — Точку входа в программу — метод Main, в котором происходит созда- ние экземпляра класса TCommandQueueTester.
Теперь создадим тест, который проверяет, создается ли объект типа TCommand, и добавляется ли команда в конец очереди. private void TCommandQueueTestl() { LogMessage("//////////// TCommandQueue Testi /////////////"); LogMessage("Проверяем, создается ли объект типа TCommand"); //В очереди нет команд dump(); // Добавляем команду // параметр = --1 означает, что команда должна быть до- бавлена в конец очереди CommandQueue.AddCommand(TCommand.GetR,0,0,0,new TBearingParam(),new TAxleParamO ,-1); LogMessage("Command added"); // В очереди одна команда dump(); } Рис. 6.7. Тест В класс включены еще два разработанных теста. После завершения теста следует просмотреть текстовый журнал теста, чтобы сравнить полученные результаты с ожидаемыми ре- зультатами, заданными в спецификации тестового случая TCommandQueueTestl (рис. 6.8). //////////////////// TCommandQueueTestl ////////////////// Проверяем, создается ли объект типа TCommand О commands in command queue Command added 1 commands in command queue 0: ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ Рис. 6.8. Спецификация результатов теста
Лекция 7. Разновидности тестирования: системное и регрессионное тестирование Рассматриваются задачи и категории тестов, применяемые в системном тестировании. Приводится пример системного тестирования. Обсуждается ре- грессионное тестирование и комбинирование различных уровней тестирования. Ключевые слова: системное тестирование, задачи системного тести- рования, категории тестов, регрессионное тестирование, перетестирова- ние, комбинирование уровней тестирования. Системное тестирование Системное тестирование качественно отличается от интеграционного и модульного уровней. Системное тестирование рассматривает тестируе- мую систему в целом и оперирует на уровне пользовательских интерфейсов, в отличие от последних фаз интеграционного тестирования, которое опери- рует на уровне интерфейсов модулей. Различны и цели этих уровней тести- рования. На уровне системы часто сложно и малоэффективно анализиро- вать прохождение тестовых траекторий внутри программы или отслеживать правильность работы конкретных функций. Основная задача системного тестирования в выявлении дефектов, связанных с работой системы в целом, таких как неверное использование ресурсов системы, непредусмотренные комбинации данных пользовательского уровня, несовместимость с окруже- нием, непредусмотренные сценарии использования, отсутствующая или неверная функциональность, неудобство в применении и тому подобное. Системное тестирование производится над проектом в целом с помо- щью метода «черного ящика». Структура программы не имеет никакого значения, для проверки доступны только входы и выходы, видимые поль- зователю. Тестированию подлежат коды и пользовательская документация. Категории тестов системного тестирования: 1. Полнота решения функциональных задач. 2. Стрессовое тестирование — на предельных объемах нагрузки входного потока. 3. Корректность использования ресурсов (утечка памяти, возврат ре- сурсов). 4. Оценка производительности. 5. Эффективность защиты от искажения данных и некорректных действий. 6. Проверка инсталляции и конфигурации на разных платформах. 7. Корректность документации.
Поскольку системное тестирование проводится на пользовательских интерфейсах, создается иллюзия того, что построение специальной сис- темы автоматизации тестирования не всегда необходимо. Однако объемы данных на этом уровне таковы, что обычно более эффективным подходом является полная или частичная автоматизация тестирования, что приво- дит к созданию тестовой системы гораздо более сложной, чем система те- стирования, применяемая на уровне тестирования модулей или их ком- бинаций. Обсуждению системной фазы тестирования, как наиболее сложной и критичной для процесса разработки, посвящен раздел 5. Пример системного тестирования приложения «Поступление подшипника на склад» В спецификации тестового случая задано состояние окружения (вход- ные данные) и ожидаемая последовательность событий в системе (ожидаемый результат). После прогона тестового случая мы получаем реальную последовательность событий в системе (рис. 7.2, рис. 7.37 пРи заданном состоянии окружения. Сравнивая фактический результат с ожидаемым, можно сделать вывод о том, прошла или не прошла тес- тируемая система испытание на заданном тестовом случае. В качест- ве ожидаемого результата будем использовать спецификацию тесто- вого случая, поскольку она определяет, как система должна функцио- нировать для заданного состояния окружения. Спецификация тестового случая №1: Состояние окружения (входные данные — X): Статус склада — 32. Пришел подшипник. Статус обмена с терминалом подшипника (0— есть подшипник) и его параметры — «Статус=О Диаметр=12». Статус обмена с терминалом оси (1 — нет оси) и ее параметры — «Статус=1 Диаметр—12». Статус команды — 0. Команда успешно принята. Сообщение от склада — Команда успешно выполнена. Ожидаемая последовательность событий (выходные данные — Y): Система запрашивает статус склада (вызов функции GetStoreStat) и получает 32. Система запрашивает параметры подшипника (вызов функции GetRollerPar) и получает Статус = 0 Диаметр=12. Система запрашивает параметры оси (вызов функции GetAxlePar^ и получает Статус = 1 Диаметр=О. Система добавляет в очередь команд склада на последнее место команду SendR (получить из приемника в ячейку) (вызов функции SendStoreCom) и получает сообщение о том, что команда успешно принята — Статус — 0. Система запрашивает склад о результатах выполнения команды (вы-
Описание случая использования системы Терминал оси Терминал подшипника Склад 2. Возврат статуса Система 1. Опрос статуса ' склада 4. Возврат характеристики'''-'--- подшипника 3. При поступлении подшипника запрос характеристики подшипника характеристики оси __ оси 8. Возврат сообщения 7. Посылка складу команды “положить подшипник” о приеме команды4^ 10. Возврат сообщения о\ результатах \ выполнения \ 9. Опрос склада о результатах выполнения команды команды \ ' 11. КОНЕЦ Рис. 7.1. Краткое описание тестируемой системы «Поступление подшипника на склад» зов функции GetStoreMessage^ и получает сообщение о том, что ко- манда успешно выполнена — Статус = 1. Выходные данные (результаты выполнения Ye) — зафиксированы в журнале теста (рис. 7.2/ ВЫЗОВ: GetStoreStat РЕЗУЛЬТАТ: 32 ВЫЗОВ: GetRollerPar РЕЗУЛЬТАТ: Статус — 0 Диаметр = 12 ВЫЗОВ: Get Axle Par РЕЗУЛЬТАТ: Статус = 1 Диаметр = О ВЫЗОВ: SendStoreCom РЕЗУЛЬТАТ: О ВЫЗОВ: GetStoreMessage РЕЗУЛЬТАТ: 1 Рис. 7.2. Журнал теста
Приведенный на рис. 7.3 тест был разработан в соответствии со специ- фикацией тестового случая №1. Детальная спецификация приведена в FS (Практикум, приложение 2), результаты прогона показаны на рис. 7.4. class Testi:Test { override public void start() { //Задаем состояние окружения (входные данные) StoreStat="32"; //Поступил подшипник RollerPar="0 NewUser Depotl 123456 1 12 1 1"; //статус обмена с терминалом подшипника (0 - есть подшипник) и его параметры AxlePar="l NewUser Depotl 123456 1 0 12 12"; //статус обмена с терминалом оси (1 - нет оси) и ее параме- тры CommandStatus="0"; //команда успешно принята StoreMessage="1"; //успешно выполнена //Получаем информацию о функционировании системы wait("GetStoreStat"); //опрос статуса склада wait("GetRollerPar"); //Получение информации о подшипнике с терминала подшипника wait("GetAxlePar"); //Получение информации об оси с терминала оси wait("SendstoreCom"); //добавление в очередь команд склада на первое место //команды GetR (получить из приемника в ячейку) wait("GetStoreMessage"); //Получение сообщения от склада о результатах выполнения ко- манды //В результате первый подшипник должен быть принят } } Рис. 4.3. Тест для системного тестирования После завершения теста следует просмотреть текстовый журнал те- ста, чтобы выяснить, какая последовательность событий в системе была реально зафиксирована (выходные данные) и сравнить их с ожида- емыми результатами, заданными в спецификации тестового случая 1. Пример журнала теста (рис. 7.2):
Test started CALLzGetStoreStat О RETURN:32 CALL:GetRollerPar RETURN:0 NewUser Depotl 123456 1 12 1 1 CALL: GetAxlePar RETURN:! NewUser Depotl 123456 1 0 12 12 CALL:SendStoreCom 1 0 0 10 0 0 RETURNS CALL:GetStoreMessage RETURN:! Рис. 7.4. Тестовый журнал для случая прогона системного теста Регрессионное тестирование Регрессионное тестирование — цикл тестирования, который произво- дится при внесении изменений на фазе системного тестирования или сопровождения продукта. Главная проблема регрессионного тестиро- вания — выбор между полным и частичным перетестированием и по- полнение тестовых наборов. При частичном перетестировании конт- ролируются только те части проекта, которые связаны с измененны- ми компонентами. На ГМП это пути, содержащие измененные узлы, и, как правило, это методы и классы, лежащие выше модифициро- ванных по уровню, но содержащие их в своем контексте. Пропуск огромного объема тестов, характерного для этапа системно- го тестирования, удается осуществить без потери качественных пока- зателей продукта только с помощью регреёсионного подхода. В дан- ном пособии регрессионному тестированию посвящен раздел 6. Пример регрессионного тестирования Получив отчет об ошибке, программист анализирует исходный код, на- ходит ошибку, исправляет ее и модулъно или интеграционно тестиру- ет результат. В свою очередь тестировщик, проверяя внесенные программистом изме- нения, должен: • Проверить и утвердить исправление ошибки. Для этого необходимо выполнить указанный в отчете тест, с помощью которого была найдена ошибка. • Попробовать воспроизвести ошибку каким-нибудь другим способом. — Протестировать последствия исправлений. Возможно, что внесенные исправления привнесли ошибку (наведенную ошибку) в код, который до этого исправно работал.
Например, при тестировании класса TCommandQueue запускаем тесты (рис. 7.3): II Тест проверяет, создается ли объект типа TCommand и //добавляется ли он в конец очереди. private void TCommandQueueTestl() // Тест проверяет добавление команд в очередь на указан- ную //позицию. Также проверяется правильность удаления команд из //очереди. private void TCommandQueueTest2() Рис. 7.5. Набор тестов класса TCommandQueue При этом первый тест выполняется успешно, а второй нет, т.е. ко- манда добавляется в конец очереди команд успешно, а на указанную по- зицию — нет. Разработчик анализирует код, который реализует тес- тируемую функциональность: if ((Position<-l)&&(Position<=?this.Items.Count)) { this.Items.Insert(Position, Command); } else { if (Position==-1) { this.Items.Add(Command); } ) Рис. 7.6. Фрагмент кода с зафиксированным при тестировании де- фектом Анализ показывает, что ошибка заключается в использовании неверно- го знака сравнения в первой строке фрагмента (помечено светлым то- ном). Далее программист исправляет ошибку (рис. 7.7). Для проверки скорректированного кода хочется пропустить только тест TCommandQueueTest2. Можно убедиться, что тест TCommandQueueTest2 будет выполняться успешно. Однако одной этой проверки недостаточно. Если мы повторим пропуск двух тестов, то при запуске первого теста, TCommandQueueTestl, будет обнаружен
if ((Position>=-l)&&(Position<=this.Items.Count)) { this.Items.Insert(Position, Command); } else { if (Position==-1) { thi s.11 ems.Add(Command); } } Рис. 7.7. Исправленный фрагмент кода новый дефект. Повторный анализ кода показывает, что ветка else не выполняется. Таким образом, исправление в одном месте привело к ошибке в другом, что демонстрирует необходимость проведения пол- ного перетестирования. Однако повторное перетестирование требу- ет значительных усилий и времени. Возникает задача — отобрать со- кращенный набор тестов из исходного набора (может быть, пополнив его рядом дополнительных — вновь разработанных — тестов), кото- рого, тем не менее, будет достаточно для исчерпывающей проверки функциональности в соответствии с выбранным критерием. Органи- зация повторного тестирования в условиях сокращения ресурсов, необ- ходимых для обеспечения заданного уровня качества продукта, обеспе- чивается регрессионным тестированием. Комбинирование уровней тестирования В каждом конкретном проекте должны быть определены задачи, ре- сурсы и технологии для каждого уровня тестирования таким образом, чтобы каждый из типов дефектов, ожидаемых в системе, был «адре- сован», то есть в общем наборе тестов должны иметься тесты, напра- вленные на выявление дефектов подобного типа. Табл. 7.1 суммирует характеристики свойств модульного, интеграционного и системного уровней тестирования. Задача, которая стоит перед тестировщиками и менеджерами, заключается в оптимальном распределении ресурсов между всеми тремя типами тестирования. Например, перенесение усилий на поиск фиксированного типа дефектов из области систем- ного в область модульного тестирования может существенно снизить сложность и стоимость всего процесса тестирования.
Табл. 7.1. Характеристики модульного, интеграционного и систем- ного тестирования Модульное Интеграционное Системное Типы дефектов Локальные дефе- кты, такие как опечатки в реа- лизации алгорит- ма, неверные операции, логи- ческие и матема- тические выра- жения, циклы, ошибки в ис- пользовании ло- кальных ресур- сов, рекурсия и т.п. Интерфейсные дефекты, такие как неверная трактовка пара- метров и их фор- мат, неверное ис- пользование сис- темных ресурсов и средств комму- никации, и т.п. Отсутствующая или некоррект- ная функцио- нальность, неудобство ис- пользования, не- предусмотренные данные и их ком- бинации, непред- усмотренные или не поддержанные сценарии работы, ошибки совмес- тимости, ошибки пользовательской документации, ошибки перено- симости продук- та на различные платформы, про- блемы произво- дительности, ин- сталляции и т.п. Необходимость в системе тестиро- вания Да Да Нет (*) Цена разработки системы тестиро- вания Низкая Низкая до уме- ренной Умеренная до высокой или неприемлемой Цена процесса тестирования, то есть разработки, прогона и анализа тестов Низкая Низкая Высокая (*) прямой необходимости в системе тестирования нет, но цена процесса системного тестирования часто настолько высока, что требует использования систем автомати- зации, несмотря на возможно высокую их стоимость.
Лекция 8. Автоматизация тестирования Рассматривается структура тестового набора для автоматического про- гона. Обсуждается структура инструментальной системы автоматизации те- стирования. Сравниваются издержки и эффективность различных методов тестирования. Ключевые слова: автоматический прогон тестов, структура тестового набора, инструментальная система автоматизации тестирования, log- файл, цикл с, издержки тестирования, эффективность тестирования. Автоматизация тестирования Использование различных подходов к тестированию определяется их эффективностью применительно к условиям, определяемым промыш- ленным проектом. В реальных случаях работа группы тестирования пла- нируется так, чтобы разработка тестов начиналась с момента согласова- ния требований к программному продукту (выпуск Requirement Book, со- держащей высокоуровневые требования к продукту) и продолжалась па- раллельно с разработкой дизайна и кода продукта. В результате к началу системного тестирования создаются тестовые наборы, содержащие тыся- чи тестов. Большой набор тестов обеспечивает всестороннюю проверку функциональности продукта и гарантирует качество продукта, но про- пуск такого количества тестов на этапе системного тестирования предста- вляет проблему. Ее решение лежит в области автоматизации тестирова- ния, т.е. в автоматизации разработки. Структура программы Р теста Загрузка теста (X,Y*) Запуск тестируемого модуля Сравнение полученных результатов Y с эталонными Y* Структура тестируемого комплекса ModF ModFl ModF2 ModF3 _ ModF31 ModF32 Структура тестирующего модуля Мод TestModF: Mod TestMoflFl Mod TestMoflF2 Mod TestMoflF3
Р TestMoflF Мод TestModFl: Р TestMoflFl Мод TestModF2: Р TestMoflF2 Мод TestModF3: Mod TestMoflF31 Mod TestMoflF32 P TestMoflF3 Рис. 8.1. Структура тестового набора в системе автоматизации тес- тирования или генерации тестового набора и в автоматизации про- пуска сгенерированных тестов на разрабатываемом приложении На рис. 8.1 приведены структура теста, структура тестируемого ком- плекса и структура тестирующего модуля. Особенностью структуры каж- дого из тестирующих модулей Mi является запуск тестирующей програм- мы Pi после того как каждый из модулей Mij, входящих в контекст моду- ля Mi, оттестирован. В этом случае запуск тестирующего модуля обеспе- чивает рекурсивный спуск к программам тестирования модулей нижнего уровня, а затем исполняет тестирование вышележащих уровней в услови- ях оттестированности нижележащих. Тестовые наборы подобной структу- ры ориентированы на автоматическое управление пропуском тестового набора в тестовом цикле. Важным преимуществом подобной организа- ции является возможность регулирования нижнего уровня, до которого следует доходить в цикле тестирования. В этом случае контекст редуциро- ванных в конкретном тестовом цикле модулей помечается как базовый, не подлежащий тестированию. Например, если контекст модуля ModF3: (ModF31, ModF32) — помечен как базовый, то в результате рекурсивный спуск затронет лишь модули ModFl, ModF2, ModF3 и вышележащий мо- дуль ModF. Описанный способ организации тестовых наборов с успехом применяется в системах автоматизации тестирования. Собственно использование эффективной системы автоматизации тестирования сокращает до минимума (например, до одной ночи) время пропуска тестов, без которого невозможно подтвердить факт роста каче- ства (уменьшения числа оставшихся ошибок) продукта. Системное тести- рование осуществляется в рамках циклов тестирования (периодов пропу- ска разработанного тестового набора над build разрабатываемого прило- жения). Перед каждым циклом фиксируется разработанный или исправ-
ленный build, на который заносятся обнаруженные в результате тестово- го прогона ошибки. Затем ошибки исправляются, и на очередной цикл тестирования предъявляется новый build. Окончание тестирования сов- падает с экспериментально подтвержденным заключением о достигнутом уровне качества относительно выбранного критерия тестирования или о снижении плотности необнаруженных ошибок до некоторой заранее ого- воренной величины. Возможность ограничить цикл тестирования преде- лом в одни сутки или несколько часов поддерживается исключительно за счет средств автоматизации тестирования. Рис. 8.2. Структура инструментальной системы автоматизации тес- тирования На рис. 8.2 представлена обобщенная структура системы автомати- зации тестирования, в которой создается и сохраняется следующая ин- формация: • Набор тестов, достаточный для покрытия тестируемого приложе- ния в соответствии с выбранным критерием тестирования — как результат ручной или автоматической разработки (генерации) те- стовых наборов и драйвер/монитор пропуска тестового набора. • Результаты прогона тестового набора, зафиксированные в log- файле. Log-файл содержит трассы («протоколы»), представляю- щие собой реализованные при тестовом прогоне последователь- ности некоторых событий (значений отдельных переменных или их совокупностей) и точки реализации этих событий на графе программы. В составе трасс могут присутствовать последователь- ности явно и неявно заданных меток, задающих пути реализации трасс на управляющем графе программы, совокупности значений переменных на этих метках, величины промежуточных результа-
тов, достигнутых на некоторых метках, и т.п. • Статистика тестового цикла, содержащая: 1) результаты пропуска каждого теста из тестового набора и их сравнения с эталонными величинами; 2) факты, послужившие основанием для принятия решения о продолжении или окончании тестирования; 3) крите- рий покрытия и степень его удовлетворения, достигнутая в цикле тестирования. Результатом анализа каждого прогона является список проблем в ви- де ошибок и дефектов, который заносится в базу развития проекта. Далее происходит работа над ошибками, где каждая поднятая проблема иденти- фицируется, относится к соответствующему модулю и разработчику, при- оритезируется и отслеживается, что обеспечивает гарантию ее решения (исправления или отнесения к списку известных проблем, решение кото- рых по тем или иным причинам откладывается) в последующих build. Ис- правленный и собранный для тестирования build поступает на следую- щий цикл тестирования, и цикл повторяется, пока нужное качество про- граммного комплекса не будет достигнуто. В этом итерационном процес- се средства автоматизации тестирования обеспечивают быстрый конт- роль результатов исправления ошибок и проверку уровня качества, дос- тигнутого в продукте. Некачественный продукт зрелая организация не производит. Издержки тестирования Интенсивность обнаружения ошибок на единицу затрат и надеж- ность тесно связаны с временем тестирования и, соответственно, с гаран- тией качества продукта (рис. 8.ЗА). Чем больше трудозатрат вкладывается в процесс тестирования, тем меньше ошибок в продукте остается незаме- ченными. Однако совершенство в индустриальном программировании имеет пределы, которые прежде всего связаны с затратами на получение программного продукта, а также с избытком качества, которое не востре- бовано заказчиком приложения. Нахождение оптимума — очень ответст- венная задача тестировщика и менеджера проекта. Движение к уменьшению числа оставшихся ошибок или к качеству продукта приводит к применению различных методов отладки и тестиро- вания в процессе создания продукта. На рис.8.ЗВ приведен затратный компонент тестирования в зависимости от совершенствования применя- емого инструментария и методов тестирования. На практике популярны следующие методы тестирования и отладки, упорядоченные по связан- ным с их применением затратам:
1. Статические методы тестирования. 2. Модульное тестирование. 3. Интеграционное тестирование. 4. Системное тестирование. 5. Тестирование реального окружения и реального времени. Зависимость эффективности применения перечисленных методов или их способности к обнаружению соответствующих классов ошибок (С) сопоставлена на рис. 8.3 с затратами (В). График показывает, что со временем, по мере обнаружения более сложных ошибок и дефектов, эф- фективность низкозатратных методов падает вместе с количеством обна- руживаемых ошибок. Отсюда следует, что все методы тестирования не только имеют пра- во на существование, но и имеют свою нишу, где они хорошо обна- руживают ошибки, тогда как вне ниши их эффективность падает. Поэтому необходимо совмещать различные методы и стратегии от- ладки и тестирования с целью обеспечения запланированного каче- ства программного продукта при ограниченных затратах, что дости- жимо при использовании процесса управления качеством про- граммного продукта.
Лекция 9. Особенности индустриального тестирования Рассматриваются особенности подхода к обеспечению качества про- граммного продукта средствами тестирования. Приводится пример и методи- ка выбора критериев качества тестирования. Определяются фазы процесса тестирования и шаги тестового цикла, применяемые в индустриальном тес- тировании. Рассматривается структура документа «Тестовый план». Рассма- триваются планируемые типы тестирования для различных частей продукта или для проверки различных характеристик продукта. Описываются подхо- ды к тестированию спецификаций и сценариев. Приводится ручной подход и подход генерации тестовых наборов при разработке тестов. Сравниваются методы автоматизации исполнения тестов. Ключевые слова: индустриальный подход к тестированию качества программного продукта, методика выбора критериев качества, фазы про- цесса тестирования, шаги тестового цикла, тестовый план, тестовые ре- сурсы, тестовая стратегия, тестовый сценарий, тестовый цикл, тестирова- ние спецификаций и сценариев, ручная разработка тестов, тестовый скрипт, генерация тестовых наборов, методы автоматизации исполнения тестов. Индустриальный подход Особенности индустриального тестирования Качество программного продукта и тестирование Качество программного продукта можно оценить некоторым набо- ром характеристик, определяющих, насколько продукт «хорош» с точки зрения всех потенциально заинтересованных в нем сторон. Такими сто- ронами являются: • Заказчик продукта. • Спонсор. • Конечный пользователь. • Разработчики продукта. • Тестировщики продукта. • Инженеры поддержки. • Отдел обучения. • Продаж и т.п.
Каждый из участников может иметь различное представление о про- дукте и по-разному судить о том, насколько он хорош или плох, то есть насколько высоко качество продукта. С точки зрения разработчика, про- дукт может быть настолько хорош, насколько хороши заложенные в нем алгоритмы и технологии. Пользователю продукта, скорее всего, безраз- личны детали внутренней реализации, его в первую очередь волнуют во- просы функциональности и надежности. Спонсора интересует цена и со- вместимость с будущими технологиями. Таким образом, задача обеспече- ния качества продукта выливается в задачу определения заинтересован- ных лиц, согласования их критериев качества и нахождения оптимально- го решения, удовлетворяющего этим критериям. В рамках подобной задачи группа тестирования рассматривается не просто как еще одна заинтересованная сторона, но и как сторона, спо- собная оценить удовлетворение выбранных критериев и сделать вывод о качестве продукта с точки зрения других участников. К сожалению, дале- ко не все критерии могут быть оценены группой тестирования. Поэтому ее внимание в основном сосредоточено на критериях, определяющих ка- чество программного продукта с точки зрения конечного пользователя. Тестирование как способ обеспечения качества. Тестирование, с тех- нической точки зрения, есть процесс выполнения приложения на неко- торых входных данных и проверка получаемых результатов с целью под- твердить их корректность по отношению к результату. Тестирование не позиционируется в качестве единственного спосо- ба обеспечения качества. Оно является частью общей системы обеспече- ния качества продукта, элементы которой выбираются по критерию наи- большей эффективности применения в конкретном проекте. Рассмотрим пример. В качестве приложения возьмем программу для ра- боты с сетью (browser), критерии качества которой приведены в табл. 9.1. Матрица критериев качества заинтересованных в них участников для рассматриваемого проекта приведена в табл. 9.2. Допустим, что вид матрицы критериев качества и проверяющих элементов системы обеспе- чения качества для данного проекта будет следующим: Данные (табл. 9.1—9.2) показывают, что из восьми элементов общего качества продукта тестирование способно оценить и контролировать только три (1,3, 4), причем наиболее эффективно тестирование контро- лирует отсутствие дефектов (3). В каждом конкретном проекте элементы системы должны быть вы- браны так, чтобы обеспечить приемлемое качество, исходя из приорите- тов и имеющихся ресурсов. Выбирая элементы для системы обеспечения качества конкретного продукта, можно применить комбинированное те- стирование, обзоры кода, аудит. При подобном выборе некоторые качест- ва, например легкость модификации и исправления дефектов, не будут
Табл. 9.1. Критерии качества программы browser Пользователь Заказчик Инженер поддержки 1. Функциональная полнота + — — 2. Цена разработки — + — 3. Отсутствие дефектов + косвенно -1- 4. Удобство использо- вания + — — 5. Возможность внесе- ния изменений в бу- дущем — косвенно + 6. Легкость исправле- ния дефектов — — + 7. Документация на ре- ализацию, в том чис- ле комментарии — — + 8. Своевременность ис- полнения проекта — + — оценены и, возможно, не будут выполнены. Задачей тестирования в рас- сматриваемом случае будет обнаружение дефектов и оценка удобства ис- пользования продукта, включая полноту функциональности. Исходя из задач, поставленных перед группой тестирования в конкретном проекте, выбирается соответствующая стратегия тестирования. Так, в данном при- мере, ввиду необходимости оценить удобство использования и полноту функциональности, преимущественный подход к разработке тестов сле- дует планировать на основе использования сценариев. Итак, основная последовательность действий при выборе и оценке критериев качества программного продукта включает: 1. Определение всех лиц, так или иначе заинтересованных в испол- нении и результатах данного проекта. 2. Определение критериев, формирующих представление о качестве для каждого из участников. 3. Приоритезацию критериев, с учетом важности конкретного участ- ника для компании, выполняющей проект, и важности каждого из критериев для данного участника. 4. Определение набора критериев, которые будут отслежены и вы- полнены в рамках проекта, исходя из приоритетов и возможно- стей проектной команды. Постановка целей по каждому из крите- риев.
Табл. 9.2. Матрица критериев качества и элементов системы обес- печения качества Тестир- ование Анализ рынка и специаль- ные лабо- ратории(*) Обзоры кода Анализ дизайна Аудиты процесса разработки 1. Полнота функ- +, не все- циональности гда эффек- тивно + — — — 2. Стоимость - разработки —- — — + 3. Отсутствие де- + фектов - + — — 4. Удобство ис- +, не пользования всегда эф- фективно + — — — 5. Возможность — внесения из- менений в бу- дущем — +- + — 6. Легкость ис- — правления де- фектов — + + — 7. Документация — на реали- зацию, в том числе коммен- тарии + + 8. Своевремен- — ность испол- нения проекта — — — + (*) имеются в виду лаборатории исследования эффективности различных сценариев использования продукта (usability labs) 5. Определение способов и механизмов достижения каждого крите- рия. 6. Определение стратегии тестирования исходя из набора критериев, подпадающих под ответственность группы тестирования, выбран- ных приоритетов и целей.
Процесс тестирования Как отмечалось в подразделе «Три фазы тестрования» (лекция 2), в тести- ровании выделяются три основных уровня, или три фазы: 1. Модульное тестирование. 2. Интеграционное тестирование. 3. Системное тестирование. Задача планирования активности тестирования состоит в оптимальном распределении ресурсов между всеми типами тестирования. В дальней- шем изложении мы сконцентрируемся на системной фазе тестирования, как на наиболее важной и критичной активности для разработки качест- венного программного продукта. Фазы процесса тестирования В процесс тестирования выделяют следующие фазы: 1. Определение целей (требований к тестированию), включающее сле- дующую конкретизацию: какие части системы будут тестировать- ся, какие аспекты их работы будут выбраны для проверки, каково желаемое качество и т.п. 2. Планирование', создание графика (расписания) разработки тестов для каждой тестируемой подсистемы; оценка необходимых чело- веческих, программных и аппаратных ресурсов; разработка распи- сания тестовых циклов. Важно отметить, что расписание тестиро- вания обязательно должно быть согласовано с расписанием разра- ботки создаваемой системы, поскольку наличие исполняемой версии разрабатываемой системы (Implementation Under Testing (IUT) или Application Under Testing (AUT) — часто употребляемые обозначения для тестируемой системы) является одним из необхо- димых условий тестирования, что создает взаимозависимость в ра- боте команд тестировщиков и разработчиков. 3. Разработка тестов, то есть тестового кода для тестируемой систе- мы, если необходимо — кода системы автоматизации тестирова- ния и тестовых процедур (выполняемых вручную). 4. Выполнение тестов: реализация тестовых циклов. 5. Анализ результатов. После анализа результатов возможно повторение процесса тестиро- вания, начиная с пунктов 3, 2 или даже 1. Тестовый цикл Тестовый цикл — это цикл исполнения тестов, включающий фазы 4 и 5 тестового процесса. Тестовый цикл заключается в прогоне разработан- ных тестов на некотором однозначно определяемом срезе системы
(состоянии кода разрабатываемой системы). Обычно такой срез системы называют build. Тестовый цикл включает следующую последовательность действий: 1. Проверка готовности системы и тестов к проведению тестового цикла, включающая: • Проверку того, что все тесты, запланированные для исполне- ния на данном цикле, разработаны и помещены в систему вер- сионного контроля. • Проверку того, что все подсистемы, запланированные для тес- тирования на данном цикле, разработаны и помещены в систе- му версионного контроля. • Проверку того, что разработана и задокументирована процеду- ра определения и создания среза системы, или build. • Проверки некоторых дополнительных критериев. 2. Подготовка тестовой машины в соответствии с требованиями, оп- ределенными на этапе планирования (например, полная очистка и переустановка системного программного обеспечения). Конфи- гурация тестовой машины, так же, как и срез системы, должны быть однозначно воспроизводимыми. 3. Воспроизведение среза системы. 4. Прогон тестов в соответствии с задокументированными процеду- рами. 5. Сохранение тестовых протоколов (test log). Test log может содержать вывод системы в STDOUT, список результатов сравнения полу- ченных при исполнении данных с эталонными или любые другие выходные данные тестов, с помощью которых можно проверить правильность работы системы 6. Анализ протоколов тестирования и принятие решения о том, про- шел или не прошел каждый из тестов (Pass/Fail). 7. Анализ и документирование результатов цикла. Последний перед выпуском продукта тестовый цикл не должен включать изменений кода build или кода продукта тестируемой системы. Этот цикл называется «финальным». Таким образом обеспечивается си- туация, когда финальный цикл полностью повторяем, а выпускаемый продукт полностью совпадает с продуктом, который прошел тестирова- ние. Финальный цикл необходим для гарантии достоверности результа- тов тестирования.
Планирование тестирования Тестовый план Тестовый план — это документ, или набор документов, содержащий следующую информацию: 1. Тестовые ресурсы. 2. Перечень функций и подсистем, подлежащих тестированию. 3. Тестовую стратегию, включающую: а) Анализ функций и подсистем с целью определения наиболее слабых мест, то есть областей функциональности тестируемой системы, где появление дефектов наиболее вероятно. Ь) Определение стратегии выбора входных данных для тестирова- ния. Так как множество возможных входных данных про- граммного продукта, как правило, практически бесконечно, выбор конечного подмножества, достаточного для проведения исчерпывающего тестирования, является сложной задачей. Для ее решения могут быть применены такие методы как по- крытие классов входных и выходных данных, анализ крайних значений, покрытие модели использования, анализ временной линии и тому подобные. Выбранную стратегию необходимо обосновать и задокументировать. с) Определение потребности в автоматизированной системе тес- тирования и дизайн такой системы. 4. Расписание тестовых циклов (пример приведен на рис. 9.1). 5. Фиксацию тестовой конфигурации: состава и конкретных пара- метров аппаратуры и программного окружения (пример приведен на рис. 9.2). 6. Определение списка тестовых метрик, которые на тестовом цикле необходимо собрать и проанализировать. Например, метрик, оце- нивающих степень покрытия тестами набора требований, степень покрытия кода тестируемой системы, количество и уровень серь- езности дефектов, объем тестового кода и другие характеристики. Типы тестирования В тестовом плане определяются и документируются различные типь| тестов. Типы тестов могут быть классифицированы по двум категориям: по тому, что именно подвергается тестированию (по виду подсистемы) и по способу выбора входных данных.
Table 1: Test Category Overview Test Categories Test to be Conducted Activities Deliverables System test cycle Tests described in items 6.1 -6.7 shall be conducted -Set up test machines -Run all the test suites -Analyze the results -Log defects -Verify resolved defects -Generate Test Report chapter -Store results in vault Updated Test Report Defect Reports Final test cycle Tests described in items 6.1 -6.7 shall be conducted -Set up test machines -Run all the test suites -Analyze the results -Verify resolved defects -Generate Test Report chapter -Store results in vault Updated Test Report Рис. 9.1. Пример расписания двух последних тестовых циклов Table 2: System Test Details Test Day Ultra Sparc 2 station 200MHz, 256M RAM 1 Prepare the test machine and the test suite for a test cycle. Perform the installation tests and the manual tests. Perform the automated tests. 2 Perform the automated tests. Perform the performance tests. Log results and time to the Test Report. Log new defects to DDTS. Store test results to Vault-msb/test/ALM/Logs. Update the Test Report. Table 3: Final Test Details Test Day Ultra Sparc 2 station 200MHz, 256M RAM 1 Perform the automated tests. Perform the performance tests. Log results and time to the Test Report. Store test results to Vault-msb/test/ALM/Logs. Verify resolved defects in DDTS. Complete the Test Report. Рис. 9.2. Пример детализации условий проведения системных циклов
Типы тестирования по виду подсистемы или продукта: 1. Тестирование основной функциональности, когда тестированию подвергается собственно система, являющаяся основным выпус- каемым продуктом. 2. Тестирование инсталляции включает тестирование сценариев пер- вичной инсталляции системы, сценариев повторной инсталляции (поверх уже существующей копии), тестирование деинсталляции, тестирование инсталляции в условиях наличия ошибок в инстал- лируемом пакете, в окружении или в сценарии и т.п. 3. Тестирование пользовательской документации включает проверку полноты и понятности описания правил и особенностей исполь- зования продукта, наличие описания всех сценариев и функцио- нальности, синтаксис и грамматику языка, работоспособность примеров и т.п. Типы тестирования по способу выбора входных значений: 1. Функциональное тестирование, при котором проверяется: • покрытие функциональных требований; • покрытие сценариев использования. 2. Стрессовое тестирование, при котором проверяются экстремаль- ные режимы использования продукта. 3. Тестирование граничных значений. 4. Тестирование производительности. 5. Тестирование на соответствие стандартам. 6. Тестирование совместимости с другими программно-аппаратны- ми комплексами. 7. Тестирование работы с окружением. 8. Тестирование работы на конкретной платформе. В реальных разработках используются и комбинируются различные типы тестов для обеспечения спланированного качества продукта. Подходы к разработке тестов Рассмотрим разные подходы к разработке тестов: два к выбору тес- товых данных, и два — к реализации тестового кода. Тестирование спецификации При разработке тестов, основанной на функциональной специфика- ции продукта, требования к продукту являются основным источником, определяющим, какие тесты будут разработаны. Для каждого требования пишется один или более тестов, которые в совокупности должны прове- рить выполнение данного требования в продукте.
Пример использования спецификации требований для разработки тестов Пусть задан следующий фрагмент набора требований для модели обме- на транзакциями: 1. Функция DoTransaction должна принимать адрес и данные в соот- ветствии с параметрами, создавать в очереди новый элемент, за- полнять его адресную часть и часть полей данных переданной инфор- мацией и инициировать транзакцию. 2. Функция DoAddressTenure должна принимать адрес в соответст- вии с параметрами, создавать в очереди новый элемент и заполнять его адресную часть. 3. Функция DoDataTenure должна принимать данные в соответствии с параметрами, находить в очереди первый элемент с частично неза- полненными полями данных, дополнять его переданной информацией и инициировать транзакцию. Концептуальное описание набора тестов, проверяющего специфика- цию, может выглядеть следующим образом'. 1. Вызвать DoTransaction с адресом и данными. Проверить появление в очереди еще одного элемента. Проверить появление на шине тран- закции с правильными адресом и данными. 2. Вызвать DoAddressTenure с адресом. Проверить появление в очере- ди еще одного элемента. Проверить отсутствие новой транзакции на шине. 3. Вызвать DoDataTenure с данными. Проверить заполнение полей дан- ных. Проверить появление на шине транзакции с правильными адре- сом и данными. Тестирование сценариев Разработка тестов, основанных на использовании сценариев, осуще- ствляется по следующей методике: 1. Определяется модель использования, включающая операционное окружение продукта и «актеров». Актером может быть пользова- тель, другой продукт, аппаратная часть и тому подобное, то есть все, с чем продукт обменивается информацией. Разделение на ок- ружение и актеров условно и служит для описания оптимальных способов использования продукта. 2. Разрабатываются сценарии использования продукта. Описание сценария в зависимости от продукта и выбранного подхода может быть строго определенным, параметризованным или разрешать
некоторую степень неопределенности. Например, описание сце- нария на языке MSC допускает задание параметризованных сце- нариев с возможностью переупорядочивания событий. 3. Разрабатывается набор тестов, покрывающих заданные сценарии. С учетом степени неопределенности, заложенной в сценарии, ка- ждый тест может покрывать один сценарий, несколько сценариев, или, наоборот, часть сценария. Использование сценариев не требует наличия полной формальной спецификации требований, но зато может потребовать больше времени на разработку и анализ. Еще одна особенность тестирования сценариев заключается в том, что этот метод направляет тестирование на проверку конкретных режи- мов использования продукта, что позволяет находить дефекты, которые метод тестирования по требованиям может пропустить. Пример использования спецификации требований для разработки тестов Так, для рассмотренного в разделе «Пример использования специфи- кации требований для разработки тестов» примера возможно создание следующего сценария и тестов: 1. Сценарий: пользователь имеет две независимые нити управления, од- на из которых отвечает за генерацию полных транзакций посредст- вом DoTransaction, а другая — за сбор транзакций из адресной час- ти и части данных, когда эта информация приходит из разных ис- точников. Таким образом, вторая нитка использует вызовы к DoAddressTenure и DoDataTenure. 2. Описание тестов: Вызвать DoAddressTenure с адресом А1, вызвать DoTransaction с адресом А2 и данными D2, вызвать DoDataTenure с данными DL Проверить последовательное появление на шине двух транзакций: {Al, D1} и {А2, D2}. При выполнении этого теста было, в частности, обнаружено, что функция DoTransaction была реализована через вызовы к DoAddressTenure и DoDataTenure, что приводило к появлению на шине транзакций вида {Al, D2} и {А2, D1}. Подобный дефект может быть обна- ружен с большим трудом, если разрабатывать тесты, основываясь только на спецификации требований. Ручная разработка тестов Наиболее распространенным способом разработки тестов является создание тестового кода вручную. Это наиболее гибкий способ разработ-
ки тестов, однако характерная для него производительность труда инже- неров-тестировщиков в создании тестового кода не намного выше скоро- сти создания кода продукта, а объемы тестового кода на практике зачас- тую превышают объем кода продукта в 10 раз. Учитывая этот факт, в сов- ременной индустрии все больше склоняются к более интеллектуальным способам получения тестового кода, таким как использование специаль- ных тестовых языков (скриптов) и генерации тестов. Генерация тестов В настоящее время некоторые языки спецификаций, используемые для описания алгоритмов тестирования, могут быть использованы для генера- ции тестового кода. Рассмотрим генерацию кода из языка MSC. Тест, опи- санный в разделе «Пример использования спецификации требований для разработки тестов», формализован на языке MSC (рис. 9.3). Здесь каждая стрелка с пометкой DoTransaction, DoAddressTenure или DoDataTenure представлет собой вызов соответствующей функции продукта с передачей параметров. Стрелка checkTr соответствует проверке прохождения по шине* транзакции с соответствующими параметрами. Каждая из стрелок диаграм- мы преобразуется в исполнимый код генератором тестов; при этом стрел- кам, представляющим собой вызовы функций, может соответствовать дос- таточно простой и маленький участок кода, вызывающий соответствующую функцию и проверяющий ее выходное значение на наличие ошибок. Следует отметить, что стрелки, соответствующие проверке транзак- ций, могут после генерации преобразоваться в достаточно сложный код, который будет выполнять ожидание появления транзакции на шине в те- чение заданного при генерации времени — тайм-аута, проверять фазы
транзакции и сверять вычисленные значения параметров с заданными эталонными значениями. В результате в рассматриваемом примере выигрыш от применения генерационного подхода достигается в основном за счет использования наглядного визуального представления тестов, что может быть нивелиро- вано затратами на создание генерационного сценария на MSC. Возможна куда более эффективная формализация MSC-сценария для генерации тестов. Рис. 9.4 представляет другой способ формализации для теста, выполняющего те же самые проверки. Рис. 9.4. Формальная запись сценарного теста на MSC с использо- ванием параллелизма В MSC на рис. 9.4 проверки транзакций сгруппированы с порождаю- щими их вызовами в отдельные фрагменты, а параллелизм, используемый при исполнении фрагментов, задан через Par — формальную конструк- цию, применяемую для изображения параллелизма в языке MSC. При ге- нерации тестов по диаграмме рис. 9.4 тестовый генератор перебирает все возможные и неповторяющиеся варианты вызова тестируемых функций, сохраняя при этом корректность порядка проверок, что в данном примере дает три сгенерированных теста. Несложно видеть, что затраты на созда- ние диаграммы рис. 9.4 не сильно отличаются от затрат на диаграмму рис. 5.3, в то время как количество тестов увеличивается в три раза. Таким образом, использование методики генерации тестового кода по формализованным MSC-диаграммам позволяет значительно поднять производительность тестирования, а также преобразовать формализацию (кодировку) сценариев в достаточно интеллектуальную деятельность.
Лекция 10. Документирование и оценка индустриального тестирования Описываются особенности документирования тестовых процедур для ручных и автоматизированных тестов, описаний тестовых наборов и тесто- вых отчетов. Рассматривается жизненный цикл дефекта. Обсуждаются мет- рики, используемые при тестировании. Ключевые слова: тестовая процедура, тестовый набор, тестовый от- чет, жизненный цикл дефекта, метрики тестирования, обзор тестов, обзор стратегии тестирования. Выполнение тестов Рассмотрим два основных подхода к выполнению тестов: подход ручного тестирования и подход автоматического исполнения (прогон) те- стов. Подходы рассмотрены на примере тестирования продукта, поддер- живающего интерфейс командной строки. Тесты описывают вызов про- дукта с параметрами и проверку возвращаемого значения в виде фикси- руемых при прогоне — текста из STDOUT и состояния некоторых файлов, зависящего от входных параметров. Ручное тестирование Ручное тестирование заключается в выполнении задокументирован- ной процедуры, где описана методика выполнения тестов, задающая по- рядок тестов и для каждого теста — список значений параметров, кото- рый подается на вход, и список результатов, ожидаемых на выходе. Пос- кольку процедура предназначена для выполнения человеком, в ее описа- ние для краткости могут использоваться некоторые значения по умолча- нию, ориентированные на здравый смысл, или ссылки на информацию, хранящуюся в другом документе. Пример фрагмента процедуры 7. Подать на вход три разных целых числа. 2. Запустить тестовое исполнение. 3. Проверить, соответствует ли полученный результат таблице [ссылка на документ 1] с учетом поправок [ссылка на документ2]. 4. Убедиться в понятности и корректности выдаваемой сопроводи- тельной информации.
Курс В приведенной процедуре тестировщик использует два дополнительных документа, а также собственное понимание того, какую сопроводительную информацию считать «понятной и корректной». Успех от использования процедурного подхода достигается в случае однозначного понимания тести- ровщиком всех пунктов процедуры. Например, в п.1 приведенной процедуры не уточняется, из какого диапазона должны быть заданы три целых числа, и не описывается дополнительно, какие числа считаются «разными». Автоматизированное тестирование Попытка автоматизировать приведенный в разделе «Пример фраг- мента процедуры» тест приводит к созданию скрипта, задающего тести- руемому продукту три конкретных числа и перенаправляющего вывод продукта в файл с целью его анализа, а также содержащего конкретное значение желаемого результата, с которым сверяется получаемое при прогоне теста значение. Таким образом, вся необходимая информация должна быть явно помещена в текст (скрипт) теста, что требует дополни- тельных по сравнению с ручным подходом усилий. Также дополнитель- ных усилий и времени требует создание разборщика вывода (программы согласования форматов представления эталонных значений из теста и вычисляемых при прогоне результатов) и, возможно, создание базы хра- нения состояний эталонных данных. Пример скрипта Приведем пример последовательности действий, закладываемых в скрипт: 1. Выдать на консоль имя или номер теста и время его начала. 2. Вызвать продукт с фиксированными параметрами. 3. Перенаправить вывод продукта в файл. 4. Проверить возвращенное продуктом значение. Оно должно быть равно ожидаемому (эталонному) результату, зафиксированному в тесте. 5. Проверить вывод продукта, сохраненный в файле (п. 3), на равенст- во заранее приготовленному эталону. 6. Выдать на консоль результаты теста в виде вердикта PASS/FAIL и в случае FAIL — краткого пояснения, какая именно проверка не про- шла. 7. Выдать на консоль время окончания теста.
Сравнение ручного и автоматизированного тестирования Результаты сравнения приведены в табл. 10.1. Сравнение показывает тенденцию современного тестирования, ориентирующую на максимальную автоматизацию процесса тестирования и генерацию тестового кода, что по- зволяет справляться с большими объемами данных и тестов, необходимых для обеспечения качества при производстве программных продуктов. Табл. 10.1. Сравнение ручного и автоматизированного подхода Ручное Автоматизированное ЗАДАНИЕ ВХОДНЫХ ЗНАЧЕНИЙ Гибкость в задании данных. Позволяет ис- пользовать разные зна- чения на разных цик- лах прогона тестов, расширяя покрытие Входные значения строго заданы « Проверка результата Гибкая, позволяет тес- тировщику оценивать нечетко сформулиро- ванные критерии Строгая. Нечетко сфор- мулированные крите- рии могут быть прове- рены только путем сравнения с эталоном Повторяемость Низкая. Человеческий фактор и нечеткое оп- ределение данных при- водят к неповторяемо- сти тестирования Высокая Надежность Низкая. Длительные тестовые циклы приво- дят к снижению внима- ния тестировщика Высокая, не зависит от длины тестового цикла / а Чувствительность к незначительным измене- ниям в продукте Зависит от детальности описания процедуры. Обычно тестировщик в состоянии выполнить тест, если внешний вид продукта и текст сооб- щений несколько изме- нились Высокая. Незначитель- ные изменения в ин- терфейсе часто ведут к коррекции эталонов Скорость выполнения тестового набора Низкая Высокая Возможность генерации тестов Отсутствует. Низкая скорость выполнения обычно не позволяет исполнить сгенериро- ванный набор тестов Поддерживается
Документация и сопровождение тестов Тестовые процедуры Тестовые процедуры — это формальный документ, содержащий описа- ние необходимых шагов для выполнения тестового набора. В случае ручных тестов тестовые процедуры содержат полное описание всех шагов и прове- рок, позволяющих протестировать продукт и вынести вердикт PASS/FAIL. 2.2 Hand Test 2.2.1 INST0021 - check license and quality status contents for the all targets. 2.2.1.1 Setup Login to the Unix C shell environment as a new created test user. 2.2.1.2 Execution [ 1 ] Change the current directory in Unix terminal window to the directory with Product files of yet untested target. [2] Enter the following command at the Unix prompt: Product.ins. [3] Enter the following answer at the Unix prompt: y. [4] Enter the following command at the Unix prompt: accept. [5] Enter the following command at the Unix prompt: accept. Following steps are only to finish the installation correctly. Just press Enter chose the default answer. [6] Press Enter. [7] Press Enter. [81 Press Enter. 2.2.1.3 Validation [ 1 ] The brief description of installation shall appear after step [2]. Prompt OK to continue? [yj: shall be active. [2] The license agreement shall appear after step [3]. Check that the con- tents of the license agreement is correct. [3] The quality status shall appear after step [4]. Check that the contents of the quality status is correct. [4] Resume and 'INSTALLATION IS SUCCESSFULLY COMPLETED' message shall appear after step [8]. 2.2.1.4 Cleanup Remove current Product target by executing Product.ins. uninstall from the directory with just tested Product files. 2.2.1.5 Log Record DDTS id numbers for any failures that were encountered. Рис. 10.1. Пример фрагмента тестовой процедуры для ручного тестирования Процедуры должны быть составлены таким образом, чтобы любой инженер, не связанный с данным проектом, был способен адекватно про- вести цикл тестирования, обладая только самыми базовыми знаниями о применяющемся инструментарии. Пример фрагмента тестовой процеду- ры для ручного тестирования приведен на рис. 10.1.
В случае описания автоматизированных тестов тестовые процедуры должны содержать достаточную информацию для запуска тестов и анали- за результатов. Пример фрагмента такой процедуры приведен на рис. 10.2. 3.2 Automated Test 3.2.1 Setup [3] Install the Product build in accordance with the readme.txt file. [4] Retrieve the test suite from CVS to user's root directory. 3.2.2 Execution [1] Change directory to ${HOME}/test/Product/cases. [2] Execute run-suite.sh command. [3] Wait for the system prompt to appear. 3.2.3 Validation [1] here must be current test state information printed on terminal. Information includes test executable name, start time and maximum time for test to work. If current system time is greater than the time which test is planned to finish by, and there is no information about test exit status, test suite is failed after step [2]. [2] check report.txt in ${HOME}/test/Product/cases directory after step [3]. This file shall contain a record for each test executed. The record shall present test by following lines: <test_name> OK If there is another string instead of "OK”, test is failed. There is the summary line at the end of file. 3.2.4 Cleanup [1] Remove the Product by executing Product.ins. uninstall from the directory with Product files. [2] Delete the ${HOME}/test/Product/cases folder with system tests. 3.2.5 Record DDTS id numbers for any failures that were encountered. Рис. 10.2. Пример фрагмента автоматизированной тестовой проце- дуры Описание тестов Описание тестов разрабатывается для облегчения анализа и под- держки тестового набора. Описание может быть реализовано в произ- вольной форме, но при этом должны выполнять следующие задачи: 1. Анализировать степень покрытия продукта тестами на основании описания тестового набора. 2. Для любой функции тестируемого продукта найти тесты, в кото- рых функция используется. 3. Для любого теста определить все функции и их сочетания, кото- рые данный тест использует (затрагивает).
4. Понять структуру и взаимосвязи тестовых файлов. 5. Понять принцип построения системы автоматизации тестирования. Документирование и жизненный цикл дефекта Каждый дефект, обнаруженный в процессе тестирования, должен быть задокументирован и отслежен. При обнаружении нового дефекта его заносят в базу дефектов. Для этого лучше всего использовать специа- лизированные базы, поддерживающие хранение и отслеживание дефек- тов — типа DDTS. При занесении нового дефекта рекомендуется указы- вать, как минимум, следующую информацию: 1. Наименование подсистемы, в которой обнаружен дефект. 2. Версия продукта (номер build), на котором дефект был найден. 3. Описание дефекта. 4. Описание процедуры (шагов, необходимых для воспроизведения дефекта). 5. Номер теста, на котором дефект был обнаружен. 6. Уровень дефекта, то есть степень его серьезности с точки зрения критериев качества продукта или заказчика. Занесенный в базу дефектов новый дефект находится в состоянии «New». После того, как команда разработчиков проанализирует дефект, он переводится в состояние «Open» с указанием конкретного разработчи- ка, ответственного за исправление дефекта. После исправления дефект переводится разработчиком в состояние «Resolved». При этом разработ- чик должен указать следующую информацию: 1. Причину возникновения дефекта. 2. Место исправления, как минимум, с точностью до исправленного файла. 3. Краткое описание того, что было исправлено. 4. Время, затраченное на исправление. После этого тестировщик проверяет, действительно ли дефект был исправлен и если это так, переводит его в состояние «Verified». Если тес- тировщик не подтвердит факт исправления дефекта, то состояние дефек- та изменяется снова на «Open». Если проектная команда принимает решение о том, что некоторый дефект исправляться не будет, то такой дефект переводится в состояние «Postponed» с указанием лиц, ответственных за это решение, и причин его принятия.
---- 11 Ч ' "" - ' -- Тестовый отчет Тестовый отчет обновляется после каждого цикла тестирования и должен содержать следующую информацию для каждого цикла: 1. Перечень функциональности в соответствии с пунктами требова- ний, запланированный для тестирования на данном цикле, и ре- альные данные по нему. 2. Количество выполненных тестов — запланированное и реально исполненное. 3. Время, затраченное на тестирование каждой функции, и общее время тестирования. 4. Количество найденных дефектов. 5. Количество повторно открытых дефектов. 6. Отклонения от запланированной последовательности действий, если таковые имели место. 7. Выводы о необходимых корректировках в системе тестов, которые должны быть сделаны до следующего тестового цикла. Пример фрагмента из тестового отчета представлен на рис. 10.3. Приведенный фрагмент отчета содержит примерные данные для четырех циклов тестирования и иллюстрирует структуру отчета. Такой вид отчет имеет после тестирования, перед началом цикла тестирования поля не за- полнены, заполнение осуществляется по окончании соответствующего цикла. Оценка качества тестов f Тесты нуждаются в контроле качества так же, как и тестируемый продукт. Поскольку тесты для продукта являются своего рода эталоном его структурных и поведенческих характеристик, закономерен вопрос о том, насколько адекватен эталон. Для оценки качества тестов использу- ются различные методы, наиболее популярные из которых кратко рас- смотрены ниже. Тестовые метрики Существует устоявшийся набор тестовых метрик, который помогает определить эффективность тестирования и текущее состояние продукта. К таким метрикам относятся следующие: 1. Покрытие функциональных требований. 2. Покрытие кода продукта. Наиболее применимо для модульного уровня тестирования. 3. Покрытие множества сценариев.
Defect Activity Number of Defects Enhance ments Severity 1 Severity 2 Severity 3 Total Enhan.&l Total 2&3 Total Newly Opened 2 5 4 0 7 4 11 Reopened 0 1 2 0 1 2 3 Newly Closed I 3 8 2 4 10 14 Total Postponed 3 0 1 0 3 1 4 Total Coverage l\pe Planned Coverage Current Version 3 Current Version 2 Current Version 1 Current Version Meadurement Functional 100% 60% 70% 90% 100% Traceability Metrix with FS Statement 75% — 70% — 76% Log-file Test Cases Summary Test Category Number of Requirements Estimated Number of Tests Number of Written Tests Functional 68 74 103 Installation 12 20 22 Boundary 5 11 11 Stress 2 15 14 Performance 1 2 3 Total 88 122 153 Summary of Activities Functional tests Function Number of tests executed Number of tests passed Tester Execetion & Analysis Time (hours) Number of Severity 2&3 Defects API 25 21 Alexandrov 2,5 2 Total 25 21 2,5 2 Рис. 10.3. Фрагмент тестового отчета 4. Количество или плотность найденных дефектов. Текущее количество дефектов сравнивается со средним для данного типа продуктов с це- лью установить, находится ли оно в пределах допустимого статистиче- ского отклонения. При этом обнаруженные отклонения как в боль-
шую, так и в меньшую сторону приводят к анализу причин их появле- ния и, если необходимо, к выработке корректирующих действий. 5. Соотношение количества найденных дефектов с количеством тес- тов на данную функцию продукта. Сильное расхождение этих двух величин говорит либо о неэффективности тестов (когда большое количество тестов находит мало дефектов) либо о плохом качест- ве данного участка кода (когда найдено большое количество дефе- ктов на не очень большом количестве тестов). 6. Количество найденных дефектов, соотнесенное по времени, или скорость поиска дефектов. Если производная такой функции близка нулю, то продукт обладает качеством, достаточным для окончания тестирования и поставки заказчику. Обзоры тестов и стратегии Тестовый код и стратегия тестирования, зафиксированные в/виде документов, заметно улучшаются, если подвергаются коллективному об- суждению. Такие обсуждения называются обзорами (review). Существует принятая в организации процедура проведения и оценки результатов об- зора. Обзоры наряду с тестированием образуют мощный набор методов борьбы с ошибками с целью повышения качества продукта. Цели обзоров тестовой стратегии и тестового кода различны. Цели обзора тестовой стратегии: 1. Установить достаточность проверок, обеспечиваемых тестирова- нием. 2. Проанализировать оптимальность покрытия или адекватность распределения количества планируемых тестов по функциональ- ности продукта. 3. Проанализировать оптимальность подхода к разработке кода, ге- нерации кода, автоматизации тестирования. Цели обзора тестового кода: 1. Установить соответствие тестового набора тестовой стратегии. 2. Проверить правильность кодирования тестов. 3. Оценить достигнутую степень качества кода, исходя из требова- ний по стандартам, простоте поддержки, наличию комментариев и т.п. 4. Если необходимо, проанализировать оптимальность тестового кода с целью удовлетворения требований к быстродействию и объему.
Лекция 11. Регрессионное тестирование: цели и задачи, условия применения, классификация тестов и методов отбора Рассматриваются цели, задачи и виды регрессионного тестирования. Перечисляются необходимые и достаточные условия применения методов выборочного регрессионного тестирования. Дается классификация методов выборочного регрессионного тестирования и самих тестов при отборе. Рас- сматриваются возможности повторного использования тестов. Ключевые слова: регрессионное тестирование, цели регрессионного тестирования, задачи регрессионного тестирования, виды регрессионно- го тестирования, регрессионный дефект, выборочное регрессионное тес- тирование, корректирующее сопровождение, адаптивное сопровожде- ние, управляемое регрессионное тестирование, выборочное регрессион- ное тестирование, отбор тестов, повторное использование тестов, новый тест, повторно используемый тест, повторно запускаемый тест, устарев- ший тест. Регрессионное тестирование ч Цели и задачи регрессионного тестирования При корректировках программы необходимо гарантировать сохра- нение качества. Для этого используется регрессионное тестирование — до- рогостоящая, но необходимая деятельность в рамках этапа сопровожде- ния, направленная на перепроверку корректности измененной програм- мы. В соответствии со стандартным определением, регрессионное тести- рование — это выборочное тестирование, позволяющее убедиться, что из- менения не вызвали нежелательных побочных эффектов, или что изме- ненная система по-прежнему соответствует требованиям. Главной задачей этапа сопровождения является реализация система- тического процесса обработки изменений в коде. После каждой модифи- кации программы необходимо удостовериться, что на функциональность программы не оказал влияния модифицированный код. Если такое влия- ние обнаружено, говорят о регрессионном дефекте. Для регрессионного тестирования функциональных возможностей, изменение которых не планировалось, используются ранее разработанные тесты. Одна из це- лей регрессионного тестирования состоит в том, чтобы, в соответствии с используемым критерием покрытия кода (например, критерием покры-
тия потока операторов или потока данных), гарантировать тот же уровень покрытия, что и при полном повторном тестировании программы. Для этого необходимо запускать тесты, относящиеся к измененным областям кода или функциональным возможностям. Пусть Т = {t], t2, tN} — множество из N тестов, используемое при первичной разработке программы Р, а T'GT — подмножество регрессион- ных тестов для тестирования новой версии программы Р'. Информация о покрытии кода, обеспечиваемом Т', позволяет указать блоки Р', требую- щие дополнительного тестирования, для чего может потребоваться по- вторный запуск некоторых тестов из множества Т2Т', или даже создание Т" — набора новых тестов для Р' — и обновление Т Другая цель регрессионного тестирования состоит в том, чтобы удо- стовериться, что программа функционирует в соответствии со своей специ- фикацией, и что изменения не привели к внесению новых ошибок в ранее протестированный код. Эта цель всегда может быть достигнута повторным выполнением всех тестов регрессионного набора, но более перспективно отсеивать тесты, на которых выходные данные модифицированной и ста- рой программы не могут различаться. Результаты сравнения выборочных методов и метода повторного прогона всех тестов приведены в табл. 11.1. Табл. 11.1. Выборочное регрессионное тестирование и повторный прогон всех тестов Повторный прогон всех тестов Выборочное регрессионное тестирование Прост в реализации Требует дополнительных расходов при внедрении Дорогостоящий и неэффективный Способно уменьшать расходы за счет исключения лишних тестов Обнаруживает все ошибки, которые Может приводить к пропуску были бы найдены при исходном ошибок тестировании — Задача отбора тестов из набора Т для заданной программы Р и изме- ненной версии этой программы Р' состоит в выборе подмножества Т'идеальное—Т Для повторного запуска на измененной программе Р', где Т' идеальное = I Р'(0 * р(0Ь Так как выходные данные Р и Р' для тес- тов из множества Т Т'цдеальное заведомо одинаковы, нет необходимости выполнять ни один из этих тестов на Р'. В общем случае, в отсутствие ди- намической информации о выполнении Р и Р' не существует методики вычисления множества Т' идеальное для произвольных множеств Р, Р' и Т. Это следует из отсутствия общего решения проблемы останова, состоя-
щей в невозможности создания в общем случае алгоритма, дающего ответ на вопрос, завершается ли когда-либо произвольная программа Р для за- данных значений входных данных. На практике создание Т'идеа1ьное воз- можно только путем выполнения на инструментированной версии Р' ка- ждого регрессионного теста, чего и хочется избежать. Реалистичный вариант решения задачи выборочного регрессионно- го тестирования состоит в получении полезной информации по результа- там выполнения Р и объединения этой информации с данными статиче- ского анализа для получения множества Т' реальное в виДе аппроксимации Т'идеальное- Этот подход применяется во всех известных выборочных ме- тодах регрессионного тестирования, основанных на анализе кода. Мно- жество Т'реддьное должно включать все тесты из Т, активирующие изме- ненный код, и не включать никаких других тестов, то есть тест tGT входит в Т' реальное тогда и только тогда, когда t задействует код Р в точке, где в Р' код был удален или изменен, или где был добавлен новый код. Если некоторый тест t задействует в Р тот же код, что и в Р', выход- ные данные Р и Р' для t различаться не будут. Из этого следует, что если P(t) * P'(t), t должен задействовать некоторый код, измененный в Р' по отношению к Р, то есть должно выполняться отношение t£T'peajlbHoe. С другой стороны, поскольку не каждое выполнение измененного кода от- ражается на выходных значениях теста, могут существовать некоторые та- кие teT'реальное, что P(t) = P'(t). Таким образом, T'pe^bHoe содержит Т' идеальное целиком и может использоваться в качестве его альтернативы без ущерба для качества тестируемого программного продукта. Важной задачей регрессионного тестирования является также уменьшение стоимости и сокращение времени выполнения тестов. Рассмотрим отбор тестов на примере рис. 11.1. Код, покрываемый те- стами, выделен цветом и штриховкой. Легко заметить, что код, покрывае- мый тестом 1, не изменился с предыдущей версии, следовательно, повтор- ное выполнение теста 1 не требуется. Напротив, код, покрываемый теста- ми 2, 3 и 4, изменился; следовательно, необходим их повторный запуск. Виды регрессионного тестирования Поскольку регрессионное тестирование представляет собой повтор- ное проведение цикла обычного тестирования, виды регрессионного тес- тирования совпадают с видами обычного тестирования. Можно говорить, например, о модульном регрессионном тестировании или о функцио- нальном регрессионном тестировании. Другой способ классификации видов регрессионного тестирования связывает их с типами сопровождения, которые, в свою очередь, опреде- ляются типами модификаций. Выделяют три типа сопровождения:
• Корректирующее сопровождение, называемое обычно исправлени- ем ошибок, выполняется в ответ на обнаружение ошибки, не тре- бующей изменения спецификации требований. При корректиру- ющем сопровождении производится диагностика и корректиров- ка дефектов в программном обеспечении с целью поддержания системы в работоспособном состоянии. void FunctionUithRemovedCode(void) { puts("Removed code"); puts("Unchanged code"); } void ChangedFunction(void) < void FunctionUithRemovedCode(void) { puts("Unchanged code"); ) void ChangedFunction(void) { ) void FunctionUithAddedCode(void) { ) void FunctionUithAddedCode(void) { puts("Unchanged code"); put s("Added code"); puts("Unchanged code ) Тест 1 Др Тест 2 Щ Тест 3 | Тест 4 Рис. 11.1. Отбор тестов для множества Т’ • Адаптивное сопровождение осуществляется в ответ на требования изменения данных или среды исполнения. Оно применяется, ко- гда существующая система улучшается или расширяется, а специ- фикация требований изменяется с целью реализации новых функций. • Усовершенствующее (прогрессивное) сопровождение включает лю- бую обработку с целью повышения эффективности работы систе- мы или эффективности ее сопровождения. В процессе адаптивного или усовершенствующего сопровождения обычно вводятся новые модули. Чтобы отобразить то или иное усовер- шенствование или адаптацию, изменяется спецификация системы. При корректирующем сопровождении, как правило, спецификация не изме- няется, и новые модули не вводятся. Модификация программы на фазе
разработки подобна модификации при корректирующем сопровожде- нии, так как из-за обнаружения ошибки вряд ли требуется менять специ- фикацию программы. За исключением редких моментов крупных изме- нений, на фазе сопровождения изменения системы обычно невелики и производятся с целью устранения проблем или постепенного расшире- ния функциональных возможностей. Соответственно, определяют два типа регрессионного тестирования: прогрессивное и корректирующее: • Прогрессивное регрессионное тестирование предполагает моди- фикацию технического задания. В большинстве случаев при этом к системе программного обеспечения добавляются новые модули. • При корректирующем регрессионном тестировании техническое задание не изменяется. Модифицируются только некоторые опе- раторы программы и, возможно, конструкторские решения. Прогрессивное регрессионное тестирование обычно выполняется после адаптивного или усовершенствующего сопровождения, тогда как корректирующее регрессионное тестирование выполняется во время тес- тирования в цикле разработки и после корректирующего сопровождения, то есть после того, как над программным обеспечением были выполнены некоторые корректирующие действия. Вообще говоря, корректирующее регрессионное тестирование должно быть более простым, чем прогрес- сивное регрессионное тестирование, поскольку допускает повторное ис- пользование большего количества тестов. Подход к отбору регрессионных тестов может быть активным или консервативным. Активный подход во главу угла ставит уменьшение объ- ема регрессионного тестирования и пренебрегает риском пропустить де- фекты. Активный подход применяется для тестирования систем с высо- кой исходной надежностью, а также в случаях, когда эффект изменений невелик. Консервативный подход требует отбора всех тестов, которые с ненулевой вероятностью могут обнаруживать дефекты. Этот подход поз- воляет обнаруживать большее количество ошибок, но приводит к созда- нию более обширных наборов регрессионных тестов. Управляемое регрессионное тестирование В течение жизненного цикла программы период сопровождения длится долго. Когда измененная программа тестируется набором тестов Т, мы сохраняем без изменений по отношению к тестированию исходной программы Р все факторы, которые могли бы воздействовать на вывод программы. Поэтому атрибуты конфигурации, в которой программа тес- тировалась последний раз (например, план тестирования, тесты tj и по- крываемые элементы MT(P,C,tj)), подлежат управлению конфигурацией.
Практика тестирования измененной версии программы Р' в тех же усло- виях, в которых тестировалась исходная программа Р, называется управля- емым регрессионным тестированием. При неуправляемом регрессионном тестировании некоторые свойства методов регрессионного тестирования могут изменяться, например, безопасный метод отбора тестов может пере- стать быть безопасным. В свою очередь, для обеспечения управляемости регрессионного тестирования необходимо выполнение ряда условий: • Как при модульном, так и при интеграционном регрессионном тестировании в качестве модулей, вызываемых тестируемым мо- дулем непосредственно или косвенно, должны использоваться реальные модули системы. Это легко осуществить, поскольку на этапе регрессионного тестирования все модули доступны в завер- шенном виде. • Информация об изменениях корректна. Информация об измене- ниях указывает на измененные модули и разделы спецификации требований, не подразумевая при этом корректность самих изме- нений. Кроме того, при изменении спецификации требований необходимо усиленное регрессионное тестирование изменив- шихся функций этой спецификации, а также всех функций, кото- рые могли быть затронуты по неосторожности. Единственным случаем когда мы вынуждены положиться на правильность изме- ненного технического задания, является изменение технического задания для всей системы или для модуля верхнего (в графе вызо- вов) уровня, при условии, что кроме технического задания, не су- ществует никакой дополнительной документации и/или какой- либо другой информации, по которой можно было бы судить об ошибке в техническом задании. • В программе нет ошибок, кроме тех, которые могли возникнуть из-за ее изменения. • Тесты, применявшиеся для тестирования предыдущих версий программного продукта, доступны, при этом протокол прогона тестов состоит из входных данных, выходных данных и траекто- рии. Траектория представляет собой путь в управляющем графе программы, прохождение которого вызывается использованием некоторого набора входных данных. Ее можно применять для оценки структурного покрытия, обеспечиваемого набором тестов. • Для проведения регрессионного тестирования с использованием существующего набора тестов необходимо хранить информацию о результатах выполнения тестов на предыдущих этапах тестиро- вания.
Предположим, что никакие операторы программы, кроме тех, чье поведение зависит от изменений, не могут неблагоприятно воздействовать на программу. Даже при таком условии существуют некоторые ситуации, требующие особого внимания, например, проблема утечки памяти и ей подобные. Ситуации такого рода в разных системах программирования обрабатываются по-разному. Например, язык Java сам по себе включает систему управления памятью. Если же система не контролирует распреде- ление памяти автоматически, мы должны считать, что все операторы ра- боты с памятью также обладают поведением, зависящим от изменений. Проблема языков типа С и C++, которые допускают произвольные арифметические операции над указателями, состоит в том, что указатели могут нарушать границы областей памяти, на которые они указывают. Это означает, что переменные могут обрабатываться способами, которые не поддаются анализу на уровне исходного кода. Чтобы учесть такие на- рушения границ памяти, выдвигаются следующие гипотезы: • Гипотеза 1 (Четко определенная память). Каждый сегмент памяти, к которому обращается система программного обеспечения, соот- ветствует некоторой символически определенной переменной. • Гипотеза 2 (Строго ограниченный указатель). Каждая переменная или выражение, используемое как указатель, должно ссылаться на некоторую базовую переменную и ограничиваться использо- ванием сегмента памяти, определяемого этой переменной. Чтобы гарантировать покрытие всех зависящих от изменений ком- понентов, для которых можно показать, что они затрагиваются существу- ющими тестами, достаточно одного теста для каждого из таких компо- нентов. Множество тестов достаточно большого размера (как правило сценарных), может способствовать обнаружению ошибок, вызванных на- рушениями условий управляемого регрессионного тестирования. Существуют и организационные условия проведения регрессионно- го тестирования. Это ресурс (время), необходимый тестовому аналитику для ознакомления со спецификацией требований системы, ее архитекту- рой и, возможно, самим кодом. Обоснование корректности метода отбора тестов Перечислим некоторые особенности реализации регрессионного те- стирования: • Некоторые участки кода программы не получают управление при выполнении некоторых тестов. • Если участок кода реализует требование, но измененный фраг- мент кода не получает управления при выполнении теста, то он и не может воздействовать на значения выходных данных програм- мы при выполнении данного теста.
• Даже если участок кода, реализующий требование, получает уп- равление при выполнении теста, это далеко не всегда отражается на выходных данных программы при выполнении данного теста. Действительно, если изменяется первый блок программы, напри- мер, путем добавления инициализации переменной, все пути в программе также изменяются, и, как следствие, требуют повтор- ного тестирования. Однако может так случиться, что только на небольшом подмножестве путей действительно используется эта инициализированная переменная. • Не каждый тест tkGT, проверяющий код, находящийся на одном пути с измененным кодом, обязательно покрывает этот изменен- ный код. • Код, находящийся на одном пути с измененным кодом, может не воздействовать на значения выходных данных измененных моду- лей программы. • Не всегда каждый оператор программы воздействует на каждый элемент ее выходных данных. Предположим, что изменения в программе ограничиваются одним оператором. Если при выполнении какого-либо теста на исходной програм- ме этот оператор никогда не получает управление, можно с уверенностью сказать, что он не получит управление и в ходе выполнения теста на новой программе, а результаты тестирования новой и старой программ будут сов- падать. Следовательно, нет необходимости выполнять этот тест на новой программе. Указанный метод легко можно обобщить для случая нескольких изменений: если тест не задействует ни одного измененного оператора, и его входные данные не изменились, код, выполняемый им в измененной программе, будет в точности таким же, как в первоначальной версии. Такой тест не выявляет различий между двумя версиями системы; следовательно, нет необходимости прогонять его повторно. Если тест не затрагивает ни од- ного оператора вывода, поведение которого зависит от измененных опера- торов, это означает, что, несмотря на изменения в программе, все операто- ры, которые получают управление при выполнении этого теста, не изменят вывод системы по отношению к предыдущей версии. Таким образом, нет необходимости повторно прогонять и тесты такого рода. Следовательно, необходимо ориентироваться на выбор только тех тестов, которые покрывают измененный код, воздействующий, в свою очередь, на вывод программы. Такой подход гарантирует, что будут вы- браны только тесты, обнаруживающие изменения, и метод будет, как го- ворят, точным.
Классификация тестов при отборе Создание наборов регрессионных тестов рекомендуется начинать с множества исходных тестов. При заданном критерии регрессионного тести- рования все исходные тесты t (tkT) подразделяются на три подмножества: 1. Множество тестов, пригодных для повторного использования. Это тесты, которые уже запускались и пригодны к использованию, но затрагивают только покрываемые элементы программы, не пре- терпевшие изменений. При повторном выполнении выходные данные таких тестов совпадут с выходными данными, полученны- ми на исходной программе. Следовательно, такие тесты не требу- ют перезапуска. 2. Множество тестов, требующих повторного запуска. К ним отно- сятся тесты, которые уже запускались, но требуют перезапуска, поскольку затрагивают, по крайней мере, один измененный по- крываемый элемент, подлежащий повторному тестированию. При повторном выполнении такие тесты могут давать результат, отлич- ный от результата, показанного на исходной программе. Множе- ство тестов, требующих повторного запуска, обеспечивает хоро- шее покрытие структурных элементов даже при наличии новых функциональных возможностей. 3. Множество устаревших тестов. Это тесты, более не применимые к измененной программе и непригодные для дальнейшего тести- рования, поскольку они затрагивают только покрываемые эле- менты, которые были удалены при изменении программы. Их можно удалить из набора регрессионных тестов. 5. Новые тесты, которые еще не запускались и могут быть использо- ваны для тестирования. Рис. 11.2 дает представление о жизненном цикле теста. Сразу после создания тест вводится в структуру базы данных как новый. После ис- полнения новый тест переходит в категорию тестов, пригодных для по- вторного использования либо устаревших. Если выполнение теста спо- собствовало увеличению текущей степени покрытия кода, тест помеча- ется как пригодный для повторного использования. В противном случае он помечается как устаревший и отбрасывается. Существующие тесты, повторно запущенные после внесения изменения в код, также класси- фицируются заново как пригодные для повторного использования или устаревшие в зависимости от тестовых траекторий и используемого кри- терия тестирования. Классификация тестов по отношению к изменениям в коде требует анализа последствий изменений. Тесты, активирующие код, затронутый изменениями, могут требовать повторного запуска или оказаться устарев-
шими. Чтобы тест был включен в класс тестов, требующих повторного за- пуска, он должен быть затронут изменениями в коде, а также должен спо- собствовать увеличению степени покрытия измененного кода по использу- емому критерию. Затронутым элементом теста может быть траектория, вы- ходные значения, или и то, и другое. Чтобы тест был включен в класс тес- тов, пригодных для повторного использования, он должен вносить вклад в увеличение степени покрытия кода и не требовать повторного запуска. Степень покрытия кода определяется для тестов, пригодных для по- вторного использования, поскольку к этому классу относятся тесты, не требующие повторного запуска и способствующие увеличению степе- ни покрытия до желаемой величины. Если имеется компонент програм- мы, не задействованный пригодными для повторного использования те- стами, то вместо них выбираются и выполняются с целью увеличения сте- пени покрытия тесты, требующие повторного запуска. После запуска та- кой тест становится пригодным для повторного использования или уста- ревшим. Если тестов, требующих повторного запуска, больше1 не оста- лось, а необходимая степень покрытия кода еще не достигнута, порожда- ются дополнительные тесты и тестирование повторяется. Окончательный набор тестов собирается из тестов, пригодных для повторного использования, тестов, требующих повторного запуска, и но- вых тестов. Наконец, устаревшие и избыточные тесты удаляются из набо- ра тестов, поскольку избыточные тесты не проверяют новые функцио- нальные возможности и не увеличивают покрытие.
Возможности повторного использования тестов К изменению существующих тестов могут привести три вида дея- тельности программистов: • Создание новых тестов. • Выполнение тестов. • Изменение кода. Поскольку каждый тест содержит входные данные, выходные дан- ные и траекторию, эти компоненты могут подвергнуться изменению в любой комбинации. При изменении входных данных существующего те- ста будем считать, что старый тест прекращает существование, и создает- ся новый тест. Таким образом, к числу разрешенных изменений теста от- носятся всевозможные пертурбации выходных данных или траекторий. Изменение выходных данных без изменения траектории и/или входных данных невозможно. Следовательно, существует только два возможных варианта изменения теста: изменение траектории или изменение траек- тории и выходных данных. В соответствии с приведенными выше рассуждениями можно выде- лить четыре уровня повторного использования теста: • Уровень Г. Тест не допускает повторного использования. Требует- ся создание нового набора тестов (например, путем удаления или изменения этого теста). • Уровень 2\ Повторно использовать возможно только входные данные теста. Во многих случаях цель тестирования состоит в активизации некоторых покрываемых элементов программы. Если из траектории существующего теста видно, что элементы программы, подлежащие покрытию, задействуются до изменен- ных команд, входные данные теста могут быть использованы повторно для покрытия этих элементов. В результате изменений в программе и/или техническом задании новая траектория и вы- ходные данные теста могут отличаться от результатов предыду- щего выполнения. Таким образом, тесты первого уровня долж- ны быть запущены повторно для получения новых выходных данных и траекторий. • Уровень 3: Возможно повторное использование как входных, так и выходных данных теста. Очевидно, что на этом уровне обычно располагаются функциональные тесты. Если модуль подвергся только изменению кода с сохранением функциональности, воз- можно повторное использование существующих функциональ- ных тестов для проверки правильности реализации. Поскольку траектория может измениться, а выходные данные — подверг- нуться воздействию со стороны изменений кода, такие тесты
должны быть запущены повторно, но ожидается получение иден- тичных результатов. • Уровень 4: Наивысший уровень повторного использования теста, предусматривающий повторное использование входных данных, выходных данных и траектории теста. В этом случае на траекто- рии теста не изменяется ни один оператор. Следовательно, в по- вторном запуске этих тестов необходимости нет, так как выход- ные данные и траектория останутся неизменными. Пример регрессионного тестирования функции решения квадратного уравнения Код этой функции приведен на рис. 11.3. Входными параметрами яв- ляются коэффициенты квадратного уравнения А, В и С, а также флаг Print, ненулевое значение которого указывает, что полученное решение необходимо вывести на экран. К выходным параметрам относятся XI и Х2, предназначенные для хранения корней уравнения, и возвращаемое зна- чение функции — дискриминант уравнения. В исходном виде функция содер- жит дефект, в результате чего уравнения с отрицательным дискриминан- том порождают ошибку времени выполнения. В новой версии функции де- фект должен быть исправлен; кроме того, необходимо реализовать запрос double Equation (int Print, float A, float B, float C, floats XI, floats X2) { float D = B*B-4.0*A*C; if (D >= 0) { XI = (-B + sqrt(D)) / 2.0 / A; X2 = (-B - sqrt(D)) I 2.0 / A; } else { XI = -B / 2.0 / A; X2 = sqrt(D); } if (Print) printf («Solution: %f, %f\n>>, XI, X2); return D; Рис. 11.3. Функция Equation — исходная версия
пользователя на изменение формата вывода решения. Код новой версии функции Equation приводится на рис. 11.4. double Equation(int Print, float A, float B, float C, floats XI, floats X2) float D = B*B-4.0*A*C; if (D >= 0) { XI = (-B + sqrt(D)) / 2.0 / A; X2 = (-B - sqrt(D)) / 2.0 / A; } else { XI = -B / 2.0 / A; X2 = sqrt(-D); } if (Print) { if (D >= 0) printf(«Solution: XI = %f, X2 = %f\n», XI, X2); else printf(«Solution: Xl = %f+%fi, X2 = %f-%fi\n», XI, X2, XI, X2); } return D; } Рис. 11.4. Функция Equation — измененная версия Существующие тесты для функции Equation приведены в табл. 6.2. Входные данные тестов представляют собой совокупность значений Print, А, В и С, подаваемых на вход функции. Выходными данными для теста являются значения XI и Х2, возвращаемое значение функции, а так- же строка, выводимая на экран; в табл. 11.2 приведены ожидаемые значе- ния выходных данных. Кроме того, для каждого теста вычисляется траек- тория его прохождения по коду. При изменении функции Equation от рис. 11.3 к рис. 11.4 меняется формат выводимых на экран данных, так что тесты 1 и 2, проверя- ющие вывод на экран, могут быть повторно использованы только на уровне 2. Тесты 3, 4 и 5могут быть использованы на уровне 3 или 4 в зависимости от результатов анализа их траектории.
Табл. 11.2. Входные и выходные данные тестов Входные данные Ожидаемые выходные данные Тест А В С Print XI Х2 Возвра- щаемое значение Выводимая строка 1 1 1 -6 1 2 -3 25 Solution: XI = 2, Х2 = -3 2 2 -3 5 1 0.75 5.567764 -31 Solution: XI - 0.75+5.567764L Х2 = 0.75-5.5677641 3 1 2 0 0 0 -2 4 4 1 2 1 0 -1 -1 0 5 1 2 2 0 -1 2 -4 Классификация выборочных методов Для проверки корректности различных подходов к регрессионному тестированию используется модель оценки методов регрессионного тес- тирования. Основными объектами рассмотрения стали полнота, точ- ность, эффективность и универсальность. Точность — мера способности метода избегать выбора тестов из Т, на которых результат выполнения измененной программы не будет отли- чаться от результата ее первоначальной версии, то есть тестов, неспособ- ных обнаруживать ошибки в Р'. Предположим, что набор Т содержит г регрессионных тестов. Из них для п тестов (п з г) поведение и результаты выполнения старой программы Р отличаются от поведения и результатов выполнения новой программы Р'. Набор тестов Т' СТ содержит m (m # 0) тестов, полученных с использованием метода отбора регрессионных тес- тов М. Из этих m тестов для 1 тестов поведение Р' и Р различается. Точ- ность Т' относительно Р, Р', Т и М, выраженная в процентах, определя- ется выражением 100 * (1 / т), тогда как соответствующий процент вы- бранных тестов определяется выражением 100 * (1 / п), если п # 0 или ра- вен 100%, если п = 0. Исходя из приведенного определения, точность множества тестов — это отношение числа тестов данного множества, на которых результаты выполнения новой и старой программ различаются, к общему числу тес- тов множества. Точность является важным атрибутом метода регрессион- ного тестирования. Неточный метод имеет тенденцию отбирать тесты, которые не должны были быть выбраны. Чем менее точен метод, тем бли- же объем выбранного набора тестов к объему исходного набора тестов.
Эффективность — оценка вычислительной стоимости стратегии вы- борочного регрессионного тестирования, то есть стоимости реализации ее требований по времени и памяти, а также возможности автоматизации. Относительной эффективностью называется эффективность метода тес- тирования при условии наличия не более одной ошибки в тестируемой программе. Абсолютной эффективностью называется эффективность ме- тода в реальных условиях, когда оценка количества ошибок в программе не ограничена. Универсальность отражает меру способности метода к применению в достаточно широком диапазоне ситуаций, встречающихся на практике. Для программы Р, ее измененной версии Р' и набора тестов Т для Р требуется, чтобы методика выборочного повторного тестирования удов- летворяла следующим критериям оценки: • Критерий 1 — Безопасность. Методика выборочного повторного тестирования должна быть безопасной, то есть должна выбирать все тесты из Т, которые потенциально могут обнаруживать ошиб- ки (все тесты, чье поведение на Р' и Р может быть различным). Безопасная методика должна рассматривать последствия добав- ления, удаления и изменения кода. При добавлении нового кода в Р, в Т могут уже содержаться тесты, покрывающие этот новый код. Такие тесты необходимо обнаруживать и учитывать при от- боре. • Критерий 2 — Точность. Стратегия повторного прогона всех тес- тов является безопасной, но неточной. В дополнение к выбору всех тестов, потенциально способных обнаруживать ошибки, она также выбирает тесты, которые ни в коем случае не могут демон- стрировать измененное поведение. В идеале, методика выбороч- ного повторного тестирования должна быть точной, то есть долж- на выбирать только тесты с изменившимся поведением. Однако для произвольно взятого теста, не запуская его, невозможно оп- ределить, изменится ли его поведение. Следовательно, в лучшем случае мы можем рассчитывать лишь на некоторое увеличение точности. Всевозможные существующие выборочные методы регрессион- ного тестирования различаются не в последнюю очередь выбором объекта или объектов, для которых выполняется анализ покры- тия и анализ изменений. Например, при анализе на уровне функ- ции при изменении любого оператора функции вся функция счи- тается измененной; при анализе на уровне отдельных операторов мы можем исключить часть тестов, содержащих вызов функции, но не активирующих измененный оператор. Выбор объектов для анализа покрытия отражается на уровне подробности анализа, а
значит, и на его точности и эффективности. Абсолютные величи- ны точности и количества выбранных тестов для заданных набо- ра тестов и множества изменений должны рассматриваться толь- ко вместе с уменьшением размера набора тестов. Небольшой про- цент выбранных тестов может быть приемлемым, только если уровень точности остается достаточно высоким. • Критерий 3 — Эффективность. Методика выборочного повторно- го тестирования должна быть эффективной, то есть должна допу- скать автоматизацию и выполняться достаточно быстро для пра- ктического применения в условиях ограниченного времени рег- рессионного тестирования. Методика должна также предусмат- ривать хранение информации о ходе исполнения тестов в мини- мально возможном объеме. • Критерий 4 — Универсальность. Методика выборочного повтор- ного тестирования должна быть универсальной, то есть примени- мой ко всем языкам и языковым конструкциям, эффективной для реальных программ и способной к обработке сколь угодно слож- ных изменений кода. В общем случае существует некоторый компромисс между без- опасностью, точностью и эффективностью. При отборе тестов анализ необходимо провести за время, меньшее, чем требуется для выполнения и проверки результатов тестов из Т, не вошедших в Т'. С учетом этого ограничения решением задачи регрессион- ного тестирования будет безопасный метод с хорошим балансом дешевизны и высокой точности.
Лекция 12. Регрессионное тестирование: разновидности метода отбора тестов Рассматриваются случайные методы, безопасные методы, методы ми- нимизации, методы, основанные на покрытии кода. Также рассматривается интеграционное регрессионное тестирование и регрессионное тестирование объектно-ориентированных программ. Ключевые слова: отбор тестов, случайные методы, безопасные мето- ды, методы минимизации, методы покрытия кода, интеграционное рег- рессионное тестирование, регрессионное тестирование объектно-ориен- тированных программ. Случайные методы Когда из-за ограничений по времени использование метода повтор- ного прогона всех тестов невозможно, а программные средства отбора те- стов недоступны, инженеры, ответственные за тестирование, могут выби- рать тесты случайным образом или на основании «догадок», то есть пред- положительного соотнесения тестов с функциональными возможностя- ми на основании предшествующих знаний или опыта. Например, если известно, что некоторые тесты задействуют особенно важные функцио- нальные возможности или обнаруживали ошибки ранее, их было бы неплохо использовать также и для тестирования измененной программы. Один простой метод такого рода предусматривает случайный отбор пре- допределенного процента тестов из Т. Подобные случайные методы при- нято обозначать random(x), где х — процент выбираемых тестов. Случайные методы оказываются на удивление дешевыми и эффек- тивными. Случайно выбранные входные данные могут давать больший разброс по покрытию кода, чем входные данные, которые используются в наборах тестов, основанных на покрытии, в одних случаях дублируя по- крытие, а в других не обеспечивая его. При небольших интервалах тести- рования их эффективность может быть как очень высокой, так и очень низкой. Это приводит и к большему разбросу статистики отбора тестов для таких наборов. Однако при увеличении интервала тестирования этот разброс становится значительно меньше, и средняя эффективность слу- чайных методов приближается к эффективности метода повторного про- гона всех тестов с небольшими отклонениями для разных попыток. Таким образом, в последнем случае пользователь случайных методов может быть более уверен в их эффективности. Вообще, детерминированные методы эффективнее случайных методов, но намного дороже, поскольку выбо-
рочные стратегии требуют большого количества времени и ресурсов при отборе тестов. Если изменения в новой версии затрагивают код, выполняемый отно- сительно часто, при случайных входных данных измененный код может в среднем активироваться даже чаще, чем при выполнении тестов, основан- ных на покрытии кода. Это приведет к увеличению метрики количества отобранных тестов для случайных наборов. Наоборот, относительно редко выполняемый измененный код активируется случайными тестами реже, и соответствующая метрика снижается. При уменьшении мощности множе- ства отобранных тестов падает эффективность обнаружения ошибок. Когда выбранное подмножество, хотя и совершенное с точки зрения полноты и точности, все еще слишком дорого для регрессионного тестиро- вания, особенно важна гибкость при отборе тестов. Какие дополнительные процедуры можно применить для дальнейшего уменьшения числа выбран- ных тестов? Одно из возможных решений — случайное исключение тестов. Однако, поскольку такое решение допускает произвольное удаление тес- тов, активирующих изменения в коде, существует высокий риск исключе- ния всех тестов, обнаруживающих ошибку в этом коде. Тем не менее, если стоимость пропуска ошибок незначительна, а интервал тестирования ве- лик, целесообразным будет использование случайного метода с небольшим процентом выбираемых тестов (25-30%), например, random(25). Вернемся к примеру регрессионного тестирования функции реше- ния квадратного уравнения. Случайный метод, такой, как random(40), мо- жет отобрать для повторного выполнения любые 2 теста из 5. Например, если будут выбраны тесты 4 и 5, изменения формата вывода на экран не будут протестированы вовсе, что вряд ли может устроить разработчика. При использовании другого случайного метода — метода экспертных оценок — в данном случае наиболее вероятен выбор всех тестов, так как за- траты на прогон невелики. Однако при регрессионном тестировании боль- ших программных систем, когда повторный прогон всех тестов неприем- лем, эксперт вынужден отсеивать некоторые тесты, что также может при- водить к тому, что часть изменений не будет протестирована полностью. Безопасные методы Метод выборочного регрессионного тестирования называется безо- пасным, если при некоторых четко определенных условиях он не исклю- чает тестов (из доступного набора тестов), которые обнаружили бы ошиб- ки в измененной программе, то есть обеспечивает выбор всех тестов, об- наруживающих изменения. Тест называется обнаруживающим измене- ния, если его выходные данные при прогоне на Р' отличаются от выход-
ных данных при прогоне на Р: P(t) х Р' (t). Тесты, активизирующие изме- ненный код, называются выполняющими изменение. Выбор всех выполняющих изменение тестов является безопасным, но при этом отбираются некоторые тесты, не обнаруживающие измене- ний. Безопасный метод может включать в Т' подмножество тестов, вы- ходные данные которых для Р и Р' ни при каких условиях не отличаются. Поскольку не существует методики, кроме собственно выполнения теста, позволяющей для любой Р' определить, будут ли выходные данные теста различаться для Р и Р', ни один метод не может быть безопасным и абсо- лютно точным одновременно. Т' является безопасным подмножеством Т тогда и только тогда, когда: P(t)#P'(t)=>tGT' Если Р и Р' выполняются в идентичных условиях и Т' является без- опасным подмножеством Т, исполнение Т’ на Р' всегда обнаруживает любые связанные с изменениями ошибки в Р, которые могут быть найде- ны путем исполнения Т. Если существует тест, обнаруживающий ошибку, безопасный метод всегда находит ее. Таким образом, ни один случайный метод не обладает такой же эффективностью обнаружения ошибок, как безопасный метод. При некоторых условиях безопасные методы в силу определения «безопасности» гарантируют, что все «обнаруживаемые» ошибки будут найдены. Поэтому относительная эффективность всех безопасных мето- дов равна эффективности метода повторного прогона всех тестов и соста- вляет 100%. Однако их абсолютная эффективность падает с увеличением интервала тестирования. Отметим, что безопасный метод действительно безопасен только в предположении корректности исходного множества тестов Т, то есть когда при выполнении всех t£T исходная программа Р за- вершилась с корректными значениями выходных данных, а все устарев- шие тесты были из Т удалены. Существуют программы, измененные версии и наборы тестов, для которых применение безопасного отбора не дает большого выигрыша в размере набора тестов. Характеристики исходной программы, изменен- ной версии и набора тестов могут совместно или независимо воздейство- вать на результаты отбора тестов. Например, при усложнении структуры программы вероятность активации произвольным тестом произвольного изменения в программе уменьшается. Безопасный метод предпочтитель- нее выполнения всех тестов набора тогда и только тогда, когда стоимость анализа меньше, чем стоимость выполнения невыбранных тестов. Для некоторых систем, критичных с точки зрения безопасности, стоимость пропуска ошибки может быть настолько высока, что небезопасные мето- ды выборочного регрессионного тестирования использовать нельзя.
Примером безопасного метода может служить метод, который выби- рает из Т каждый тест, выполняющий, по крайней мере, один оператор, до- бавленный или измененный в Р' или удаленный из Р. Применение этого метода для регрессионного тестирования функции решения квадратного уравнения потребует построения матрицы покрытия, пример которой при- веден в табл. 12.1. Следует отметить, что матрица покрытия соответствует исходной версии программы, поскольку аналогичная информация для но- вой версии программы пока не собрана. Звездочка в ячейке таблицы озна- чает, что соответствующий тест покрывает определенную строку кода; если тест не покрывает строку кода, ячейка оставлена пустой. Строки, изменен- ные по отношению к исходной версии, выделены цветом. Легко заметить, что в соответствии с требованиями предложенного безопасного метода для повторного выполнения должны быть отобраны тесты 1, 2 и 5. Методы минимизации Процедура минимизации набора тестов ставит целью отбор мини- мального (в терминах количества тестов) подмножества Т, необходимого для покрытия каждого элемента программы, зависящего от изменений. Для проверки корректности программы используются только тесты из минимального подмножества. Табл. 12.1. Матрица покрытия тестируемого кода Тест № Строка кода 1 2 3 4 5 1 Double Equation(int Print, float A, float C, floats XI, floats X2) { float B, * ♦ * * ♦ 2 float D=B*B-4.0*A*C; ♦ ♦ * ♦ * 3 if (D >= 0) { ♦ * * * ♦ 4 XI = (-B + sqrt(D)) / 2.0 / A; * * * 5 X2 = (-B - sqrt(D)) / 2.0 / A; } else { * * * 6 XI = -B / 2.0 / A; * * 7 X2 = sqrt(D); } ♦ * 8 if (Print) ♦ ♦ * * ♦ 9 printf("Solution: %f, %f\n", XI, X2) ; * ♦ 10 return D; * * * * ♦ 11 } * * * * * Обоснование применения методов минимизации состоит в следую- щем:
• Корреляция между эффективностью обнаружения ошибок и по- крытием кода выше, чем между эффективностью обнаружения ошибок и размером множества тестов. Неэффективное тестиро- вание, например многочасовое выполнение тестов, не увеличива- ющих покрытие кода, может привести к ошибочному заключе- нию о корректности программы. • Независимо от способа порождения исходного набора тестов, его минимальные подмножества имеют преимущество в размере и эффективности, так как состоят из меньшего количества тестов, не ослабляя при этом способности к обнаружению ошибок или снижая ее незначительно. • Вообще говоря, сокращенный набор тестов, отобранный при ми- нимизации, может обнаруживать ошибки, не обнаруживаемые сокращенным набором того же размера, выбранным случайным или каким-либо другим способом. Такое преимущество миними- зации перед случайными методами в эффективности является за- кономерным. Однако из всех детерминированных методов мини- мизация приводит к созданию наименее эффективных наборов тестов, хотя и самых маленьких. В частности, безопасные методы эффективнее методов минимизации, хотя и намного дороже. Минимизация набора тестов требует определенных затрат на анализ. Если стоимость этого анализа больше затрат на выполнение некоторого порогового числа тестов, существует более дешевый случайный метод, обеспечивающий такую же эффективность обнаружения ошибок. Хотя минимальные наборы тестов могут обеспечивать структурное покрытие измененного кода, зачастую они не являются безопасными, по- скольку очевидно, что некоторые тесты, потенциально способные обна- руживать ошибки, могут остаться за чертой отбора. Набор функциональ- ных тестов обычно не обладает избыточностью в том смысле, что никакие два теста не покрывают одни и те же функциональные требования. Если тесты исходно создавались по критерию структурного покрытия, мини- мизация приносит плоды, но когда мы имеем дело с функциональными тестами, предпочтительнее не отбрасывать тесты, потенциально способ- ные обнаруживать ошибки. В существующей практике тестирования ин- женеры предпочитают не заниматься минимизацией набора тестов. Многие критерии покрытия кода фактически не требуют выбора ми- нимального множества тестов. В некотором смысле, о безопасных страте- гиях и стратегиях минимизации можно думать как о находящихся на двух полюсах множества стратегий. На практике, использование «почти ми- нимальных» наборов тестов может быть удовлетворительным. Стремле- ние к сокращению объема набора тестов основано на интуитивном пред- положении, что неоднократное повторное выполнение кода в ходе мо-
|ЦИ— I =-., — —. . „Л1-... . - 1 - ' I.. ..— - —Г11 ‘ — дульного тестирования «расточительно». Однако усилия, требуемые для минимизации набора тестов, могут быть существенны, и, следовательно, могут не оправдывать затрат. Отметим, что большинство стратегий выбо- рочного регрессионного тестирования, описанных в литературе, в об- щем-то, не зависит от критерия покрытия, возможно, использовавшего- ся при создании исходного набора тестов. Инженеры, занимающиеся ре- грессионным тестированием, часто не имеют информации о том, как раз- рабатывался исходный набор тестов. Обнаружение ошибок важно для приложений, где стоимость выпол- нения тестов очень высока, в то время как стоимость пропуска ошибок считается незначительной. В этих условиях использование методов ми- нимизации целесообразно, поскольку они связаны с отбором небольшо- го количества тестов. Примером применения методов минимизации слу- жит метод, выбирающий из Т не менее одного теста для каждого операто- ра программы, добавленного или измененного при создании Р'. В табл. 6.3 для случая регрессионного тестирования функции Equation данный метод ограничится отбором одного теста — теста 2, так как этот тест по- крывает обе измененные строки. Методы, основанные на покрытии кода Значение методов, основанных на покрытии кода, состоит в том, что они гарантируют сохранение выбранным набором тестов требуемой сте- пени покрытия элементов Р' относительно некоторого критерия струк- турного покрытия С, использовавшегося при создании первоначального набора тестов. Это не означает, что если атрибут программы, определен- ный С, покрывается первоначальным множеством тестов, он будет также покрыт и выбранным множеством; гарантируется только сохранение про- цента покрываемого кода. Методы, основанные на покрытии, уменьша- ют разброс по покрытию, требуя отбора тестов, активирующих труднодо- ступный код, и исключения тестов, которые только дублируют покрытие. Поскольку на практике критерии покрытия кода обычно применяются для отбора единственного теста для каждого покрываемого элемента, подходы, основанные на покрытии кода, можно рассматривать как спе- цифический вид методов минимизации. Разновидностью методов, основанных на покрытии кода, являются методы, которые базируются на покрытии потока данных. Эти методы эффективнее методов минимизации и почти столь же эффективны, как безопасные методы. В то же время, они могут требовать, по крайней ме- ре, такого же времени на анализ, как и наиболее эффективные безопас- ные методы, и, следовательно, могут обходиться дороже безопасных ме- тодов и намного дороже других методов минимизации. Они имеют тен-
денцию к включению избыточных тестов в набор регрессионных тестов для покрытия зависящих от изменений пар определения-использования, что, в некоторых случаях, ведет к большому числу отобранных тестов. Этот факт зафиксирован экспериментально. Методы, основанные на использовании потока данных, могут быть полезны и для других задач регрессионного тестирования, кроме отбора тестов, например, для нахождения элементов Р, недостаточно тестируе- мых Т'. Метод стопроцентного покрытия измененного кода аналогичен ме- тоду минимизации. Так, для примера из табл. 12.1 существует 4 способа отобрать 2 теста в соответствии с этим критерием. Одного теста недоста- точно. Результаты сравнения методов выборочного регрессионного тес- тирования приведены в табл. 12.2. Табл. 12.2. Сравнение методов выборочного регрессионного тести- рования. Класс методов Случайные Безопасные Минимизации Покрытия Полнота ОтО%до 100% 100% < 100% < 100% Размер набо- ра тестов Настраива- ется Большой Небольшой Зависит от параметров метода Время выпол- нения метода Пренебрежи- мо мало Значительное Значительное Значительное Перспектив- ные свойства методов рег- рессионного тестирования Отсутствие средства под- держки рег- рессионного тестирования Высокие тре- бования по качеству Стоимость пропуска ошибки неве- лика Набор исход- ных тестов создается по критерию по- крытия
Лекция 13. Регрессионное тестирование: методики, не связанные с отбором тестов и методики порождения тестов Рассматривается метод уменьшения объема тестируемой программы, методы упорядочения тестов, а также круг вопросов, связанных с целесооб- разностью регрессионного тестирования, а также методика порождения но- вых тестов на основе анализа подозрительных состояний и сценарий ее при- менения. Ключевые слова: метод уменьшения объема тестируемой программы, методы упорядочения тестов, целесообразность регрессионного тестиро- вания, методика порождения тестов, анализ подозрительных состояний, сценарий применения. Интеграционное регрессионное тестирование С появлением новых направлений в разработке программного обес- печения (например, объектно-ориентированного программирования), поощряющих использование большого количества маленьких процедур, повышается важность обработки межмодульного влияния изменений ко- да для методик уменьшения стоимости регрессионного тестирования. Для решения этой задачи необходимо рассматривать зависимости по глобаль- ным переменным, когда переменной в одной или нескольких процедурах присваивается значение, которое затем используется во многих других процедурах. Такую зависимость можно рассматривать как зависимость ме- жду процедурами по потоку данных. Также возможны межмодульные за- висимости по ресурсам, например по памяти, когда ресурс разделяется ме- жду несколькими процедурами. Отметим, что при системном регрессион- ном тестировании зависимости такого рода можно игнорировать. Если изменение спецификации требований затрагивает глобальную переменную, могут потребоваться новые модульные тесты. В противном случае, повторному выполнению подлежат только модульные тесты, за- трагивающие как измененный код, так и операторы, содержащие ссылку на глобальную переменную. Брандмауэр можно определить как подмножество графа вызовов, со- держащее измененные и зависящие от изменений процедуры и интер- фейсы. Методы отбора тестов, использующие брандмауэр, требуют по- вторного интеграционного тестирования только тех процедур и интер- фейсов, которые непосредственно вызывают или вызываются из изме- ненных процедур.
Регрессионное тестирование объектно-ориентированных программ Объектно-ориентированный подход стимулирует новые приложения методик выборочного повторного тестирования. Действительно, при из- менении класса необходимо обнаружить в наборе тестов класса только те- сты, требующие повторного выполнения. Точно так же при порождении нового класса из существующего необходимо определить тесты из множе- ства тестов базового класса, требующие повторного выполнения на клас- се-потомке. Хотя благодаря инкапсуляции вероятность ошибочного взаи- модействия объектно-ориентированных модулей кода уменьшается, тем не менее, возможно, что тестирование прикладных программ выявит ошибки в методах, не найденные при модульном тестировании методов. В этом случае необходимо рассмотреть все прикладные программы, ис- пользующие измененный класс, чтобы продемонстрировать, что все су- ществующие тесты, способные обнаруживать ошибки в измененных клас- сах, были запущены повторно и было выбрано безопасное множество тес- тов. При повторном тестировании прикладных программ, классов или их наследников применение методик выборочного повторного тестирования к существующим наборам тестов может принести немалую пользу. В объектно-ориентированной программе вызов метода во время вы- полнения может быть сопоставлен любому из ряда методов. Для заданно- го вызова мы не всегда можем статически определить метод, с которым он будет связан. Выборочные методы повторного тестирования, которые по- лагаются на статический анализ, должны обеспечивать механизмы для разрешения этой неопределенности. Уменьшение объема тестируемой программы Еще один путь сокращения затрат на регрессионное тестирование со- стоит в том, чтобы вместо повторного тестирования (большой) изменен- ной программы с использованием соответственно большого числа тестов доказать, что измененная программа адекватно тестируется с помощью вы- полнения некоторого (меньшего) числа тестов на остаточной программе. Остаточная программа создается путем использования графа зависимости системы вместо графа потока управления, что позволяет исключить ненужные зависимости между компонентами в пределах одного пути графа потока управления. Так, корректировка какого-либо оператора в идеале должна приводить к необходимости тестировать остаточную программу, состоящую из всех операторов исходной программы, способных повлиять на этот оператор или оказаться в сфере его влияния. Для получения оста- точной программы необходимо знать место корректировки (в терминах
Лекция 13 Методики, не связанные с отбором тестов и методики порождения тестов операторов), а также информационные и управляющие связи в программе. Этот подход работает лучше всего для малых и средних изменений больших программ, где высокая стоимость регрессионного тестирования может за- ставить вообще отказаться от его проведения. Наличие дешевого метода остаточных программ, обеспечивающего такую же степень покрытия кода, делает регрессионное тестирование успешным даже в таких случаях. Метод остаточных программ имеет ряд ограничений. В частности, он не работает при переносе программы на машину с другим процессором или объемом памяти. Более того, он может давать неверные результаты и на той же самой машине, если поведение программы зависит от адреса ее началь- ной загрузки, или если для остаточной программы требуется меньше памя- ти, чем для измененной, и, соответственно, на остаточной программе про- ходит тест, который для измененной программы вызвал бы ошибку нехват- ки памяти. Исследования метода на программах небольшого объема пока- зали, что выполнение меньшего количества тестов на остаточной програм- ме не оправдывает затрат на отбор тестов и уменьшение объема программы. Однако для программ с большими наборами тестов это не так. Для теста 1 табл. 12.1 для функции Equation остаточная программа выглядит так, как показано в табл. 13.1. Нумерация строк оставлена такой же, как в исходной программе. Таким образом, можно заметить, что были удалены строки 6 и 7, которые не затрагиваются тестом 1 в ходе его выпол- нения, а также строки 3 и 8, содержащие вычисление предикатов, которые в ходе выполнения теста всегда истинны. Запуск теста на полной изменен- ной программе и на остаточной программе приводит к активизации одних и тех же операторов, поэтому выигрыша во времени получить не удается, однако за счет сокращения объема программы уменьшается время компи- ляции. Для нашего примера этот выигрыш незначителен и не оправдыва- ет затрат на анализ, необходимый для уменьшения объема. Таким обра- Табл. 13.1. Остаточная программа № Строка кода 1 double Equationtint Print, float A, float B, float C, floats XI, float& X2) { 2 float D = B*B-4.0*A*C; 4 XI = (-B + sqrt(D)) / 2.0 / A; 5 X2 = (-B - sqrt(D)) / 2.0 / A; if f (:"Sy:Vjte^on: $f, %f\n" XI, X2) j 10 return D;
Курс Основы тестирования программного обеспечения зом, рассмотренная технология рекомендуется к применению, прежде всего, в случаях, когда стоимость компиляции относительно высока. Сведения о методике уменьшения объема тестируемой программы приведены в табл. 13.2. Табл. 13.2. Результаты применения методики уменьшения объема Характеристика Изменение в результате применения методики Время компиляции тестируемой программы Уменьшается Время выполнения тестируемой программы Не изменяется Время работы метода отбора Увеличивается Риск пропуска ошибок Увеличивается Результаты применения методики на практике Отрицательные Методы упорядочения Методы упорядочения позволяют инженерам-тестировщикам рас- пределить тесты так, что тесты с более высоким приоритетом исполня- ются раньше, чем тесты с более низким приоритетом, чтобы затем огра- ничиться выбором первых п тестов для повторного исполнения. Это особенно важно для случаев, когда тестировщики могут позволить себе повторное выполнение только небольшого количества регрессионных тестов. Одной из проблем упорядочения тестов является отсутствие пред- ставлений о том, скольких тестов в конкретном проекте достаточно для отбора. В отличие от подхода минимизации, использующего все тесты минимального набора, оптимальное число тестов в упорядоченном набо- ре неизвестно. Возникает проблема баланса между тем, что необходимо делать в ходе регрессионного тестирования, и тем, что мы можем себе по- зволить. В итоге количество запускаемых тестов определяется ограниче- ниями по времени и бюджету и порядком тестов в наборе. С учетом этих факторов следует запускать повторно как можно больше тестов, начиная с верхней строки списка упорядоченных тестов. Возможное преимущество упорядочения тестов состоит в том, что сложность соответствующего алгоритма, О(п2) в наихудшем случае, мень- ше, чем сложность алгоритма минимизации, который в некоторых случа- ях может требовать экспоненциального времени выполнения. Проблему упорядочения тестов можно сформулировать следующим образом:
• Дано: Т — набор тестов, РТ — набор перестановок Т, f— функция из РТ на множество вещественных чисел. • Найти: набор T'GPT такой, что: (VT") (T"GPT) (Т" #Т') [f(T') г f(T")] В приведенном определении РТ представляет собой множество всех возможных вариантов упорядочения — Т, a f — функция, которая, будучи применена к любому такому упорядочению, выдает его вес. (Предполо- жим, что большие значения весов предпочтительнее малых.) Упорядочение может преследовать различные цели: — Увеличение частоты обнаружения ошибок наборами тестов, то есть увеличение вероятности обнаружить ошибку раньше при выполнении регрессионных тестов из этих наборов. — Ускорение процесса покрытия кода тестируемой системы и дости- жение требуемой степени покрытия кода на более ранних этапах процесса тестирования. — Быстрейший рост вероятности того, что тестируемая система на- дежна. — Увеличение вероятности обнаружения ошибок, связанных с конкрет- ными изменениями кода, на ранних этапах процесса тестирования и т.п. Методы упорядочения планируют выполнение тестов в процессе ре- грессионного тестирования в порядке, увеличивающем их эффектив- ность в терминах достижения заданной меры производительности. Обос- нование использования упорядочивающего метода состоит в том, что упорядоченный набор тестов имеет большую вероятность достижения це- ли, чем тесты, расположенные по какому-либо другому правилу или в случайном порядке. Различают два типа упорядочения тестов: общее и в зависимости от версии. Общее упорядочение тестов при данных программе Р и наборе тестов Т подразумевает нахождение порядка тестов Т, который окажется полезным для тестирования нескольких последовательных измененных версий. Считается, что для этих версий итоговый упорядоченный набор тестов позволяет в среднем быстрее достигнуть цели, ради которой про- изводилось упорядочение, чем исходный набор тестов. Однако имеется значительный объем статистических свидетельств в пользу наличия свя- зи между частотой обнаружения ошибок и конкретной версией тестиру- емой программы: разные версии программы предоставляют различные возможности по упорядочению тестов. При регрессионном тестирова- нии мы имеем дело с конкретной версией программного продукта и хо-
тим упорядочивать тесты наиболее эффективно по отношению именно к этой версии. Например, упорядочивать тесты можно по количеству покрывае- мых ими изменений кода. При безопасном отборе тестов из табл. 12.1 бу- дут выбраны тесты 1, 2 и 5, из которых наиболее приоритетным является тест 2, так как он затрагивает оба изменения, тогда как тесты 1 и 5 — толь- ко одно. Сведения о методике упорядочения тестов суммированы в табл. 13.3. Табл. 13.3. Результаты применения методики упорядочения тестов Характеристика Изменение в результате применения методики Время работы метода отбора Увеличивается незначительно Частота обнаружения ошибок Увеличивается Скорость покрытия кода Увеличивается Результаты применения методики на практике Положительные Целесообразность отбора тестов Поскольку в общем случае оптимальный отбор тестов (то есть выбор в точности тех тестов, которые обнаруживают ошибку) невозможен, соот- ношение между затратами на применение методов выборочного регрес- сионного тестирования и выигрышем от их использования является ос- новным вопросом практического применения выборочного регрессион- ного тестирования. На основании оценки этого соотношения делается вывод о целесообразности отбора тестов. Эффективное регрессионное тестирование представляет собой ком- промисс между качеством тестируемой программы и затратами на тести- рование. Чем больше регрессионных тестов, тем полнее проверка пра- вильности программы. Однако большее количество выполняемых тестов обычно означает увеличение финансовых затрат и времени на тестирова- ние, что на практике не всегда приемлемо. Выполнение меньшего коли- чества регрессионных тестов может оказаться дешевле, но не позволяет гарантировать сохранение качества. Когда отдельные модули невелики и несложны, а связанные с ни- ми наборы тестов также небольшие, простой повторный запуск всех те- стов достаточно эффективен. При интеграционном тестировании это менее вероятно. В то время как тесты для отдельных модулей могут быть небольшими, тесты для групп модулей и подсистем достаточно ве-
Лекция 13 Методики, не связанные с отбором тестов и методики порождения тестов лики, что создает предпосылки для уменьшения издержек тестирова- ния. С другой стороны, с ростом размера приложений стоимость при- менения выборочной стратегии повторного тестирования может воз- расти до неприемлемой величины. Затраты на необходимый для отбора анализ могут перевешивать экономию от прогона сокращенного набо- ра тестов и анализа результатов прогона. Однако в области тестирова- ния достаточно больших программ положительный баланс затрат и вы- год вполне достижим. Модель затрат и выгод при использовании выборочных стратегий регрессионного тестирования должна учитывать прямые и косвенные за- траты. Прямые затраты включают отбор и выполнение тестов и анализ ре- зультатов. Косвенные затраты включают затраты на управление, сопрово- ждение баз данных и разработку программных средств. Выгоды — это за- траты, которых удалось избежать, не выполняя часть тестов. Чтобы метод выборочного регрессионного тестирования был эффективнее метода по- вторного прогона всех тестов, стоимость анализа при отборе подмножест- ва тестов вкупе со стоимостью их выполнения и проверки результатов должна быть меньше, чем стоимость выполнения и проверки результатов исходного набора тестов. Пусть Т' — подмножество Т, отобранное некоторой стратегией вы- борочного регрессионного тестирования М для программы Р, | Т'| — обо- значает мощность Т', s — средняя стоимость отбора одного теста в резуль- тате применения МкР для создания Т', а г — средняя стоимость выпол- нения одного теста из Т на Р и проверки его результата. Тогда для того, чтобы выборочное регрессионное тестирование было целесообразным, требуется выполнение неравенства: s | Т'| < г (| Т | — | Т'|) Применяя вышеупомянутую модель стоимости с целью анализа затрат, полезно условно разделять регрессионное тестирование на две фазы — предварительную и критическую. Предварительная фаза рег- рессионного тестирования начинается после выпуска очередной вер- сии программного продукта; во время этой фазы разработчики рас- ширяют функциональность программы и исправляют ошибки, гото- вясь к выпуску следующей версии. Одновременно тестировщики мо- гут планировать будущее тестирование или выполнять задачи, требу- ющие наличия только предыдущей версии программы, такие как сбор тестовых траекторий и анализ покрытия. Как только в программу вне- сены исправления, начинается критическая фаза регрессионного тес- тирования. В течение этой фазы регрессионное тестирование новой версии программы является доминирующим процессом, время кото- рого обычно ограничено моментом поставки заказчику. Именно на
критической фазе регрессионного тестирования наиболее важна ми- нимизация затрат. При использовании выборочного метода регресси- онного тестирования важно использовать факт наличия этих двух фаз, уделяя как можно больше внимания выполнению задач, связанных с анализом, в течение предварительной фазы, чтобы на критической фазе заниматься только прогоном тестов и уменьшить вероятность срыва сроков поставки. Тем не менее, важно понимать, что до внесе- ния последнего изменения в код анализ может быть выполнен только частично. Если не учитывать не очень больших затрат на анализ при использо- вании детерминированных методов, решение о применении конкретного метода отбора тестов будет зависеть от отношения стоимости выполнения большего количества тестов к цене пропуска ошибки, что зависит от мно- жества факторов, специфических для каждого конкретного случая. При отсутствии ошибок сбережения пропорциональны уменьшению размера набора тестов и могут быть измерены в терминах процента выбранных те- стов, | Т'| /1Т |. Модели стоимости могут использоваться как при выборе наилучшей стратегии, так и для оценки пригодности конкретной стратегии. При ана- лизе учитываются такие факторы как размер программы (в строках кода), мощность множества регрессионных тестов и количество покрываемых элементов, задействованных исходным множеством тестов. Общий метод исследования проблемы целесообразности отбора тес- тов состоит в нахождении или создании исходной и измененной версий некоторой системы и соответствующего набора тестов. В этих условиях применяется методика отбора тестов, и размер и эффективность выбран- ного набора тестов сравнивается с размером и эффективностью первона- чального набора тестов. Результаты показывают, что применение методов отбора регрессионных тестов, в том числе и безопасных, не всегда целе- сообразно, поскольку затраты и выгоды от их использования изменяются в широком диапазоне в зависимости от многих факторов. На практике наборы, основанные на покрытии, обеспечивают лучшие результаты от- бора тестов. Разумеется, отношение покрытия — не единственный фактор, кото- рый может отразиться на целесообразности применения выборочного ре- грессионного тестирования. Для некоторых приложений создание усло- вий для тестирования (в том числе компиляция и загрузка модулей и ввод данных) может обходиться намного дороже, чем вычислительные ресур- сы для непосредственного исполнения тестируемой системы. Например, в телекоммуникационной промышленности стоимость создания тесто- вой лаборатории для моделирования реальной сети связи может дости- гать нескольких миллионов долларов.
Подсчет порога целесообразности помогает определить, может ли отбор тестов вообще быть целесообразен для данного программного изделия и набора тестов. Однако даже в случаях, когда значение поро- га целесообразности указывает, что отбор тестов может быть целесооб- разен, он не обязательно будет таковым; результат зависит от парамет- ров набора тестов, таких как размер набора, характеристики покрытия кода, уровень подробности и время выполнения тестов, а также от ме- стоположения изменений. Существенно повлиять на общую оценку могут затраты на оплату труда тестового персонала, доступность сво- бодного машинного времени для регрессионного тестирования, дос- тупность стенда, на котором развернуто программное обеспечение приложения и т.п. Отметим, что стоимость прогона тестов связана не столько с размером программы, сколько с ограничениями на допу- стимое время прогона. В некоторых случаях, когда число тестов, отброшенных выбороч- ным методом регрессионного тестирования незначительно, но его при- менение тем не менее заслуживает внимания. Дело в том, что любое со- кращение высокозатратного времени использования тестовой лаборато- рии особенно важно, а для отбора тестов используются другие ресурсы. Подобные обстоятельства необходимо включать в оценку стоимости ана- лиза путем учета не только стоимости эксплуатации ресурса, но и таких факторов как время суток, день недели, время, оставшееся до выпуска очередной версии продукта и т.п. В этом случае модель стоимости долж- на соблюдать баланс между высокой стоимостью прогона тестов в тесто- вой лаборатории и относительно небольшой стоимостью проведения ана- лиза на незанятых компьютерах. Для некоторых программ и наборов тестов выборочное тестирова- ние неэффективно, так как порог целесообразности превышает число те- стов в наборе. В таких случаях методы отбора тестов, независимо от того, насколько успешно они уменьшают число тестов, требующих повторного выполнения, не могут давать экономию. Этот результат отражает тот факт, что целесообразность отбора зависит как от стоимости анализа, так и от стоимости выполнения тестов. Возможность достижения экономии при отборе регрессионных тестов для конкретной системы программного обеспечения и конкретного набора тестов должна оцениваться комплекс- но с учетом всех влияющих на решение факторов. Стоит заметить, что целесообразность применения выборочного ме- тода регрессионного тестирования нельзя воспринимать как нечто само собой разумеющееся. Следует очень осторожно подходить к оценке целе- сообразности отбора повторно прогоняемых тестов. В ряде случаев, когда или получаемое число остаточных тестов близко к первоначальному их количеству, или накладные расходы на повторное тестирование незначи-
тельны, выгоднее прогонять заново все тесты, особенно если прогон тес- тов полностью автоматизирован. Функции предсказания целесообразности На практике не существует способа в точности предсказать, сколь- ко тестов будет выбрано при минимизации (или по результатам приме- нения любой другой методики отбора тестов). Когда количество тестов, отсеянных по результатам отбора, незначительно, ресурсы, потраченные на отбор тестов, пропадают впустую. В таких случаях говорят, что выбо- рочное регрессионное тестирование нецелесообразно. Чтобы выяснить, заслуживает ли внимания попытка применения выборочного метода ре- грессионного тестирования, необходимо использовать прогнозирующую функцию. Прогнозирующие функции основаны на покрытии кода. Для их вы- числения используется информация о доле тестов, активирующих покры- ваемые сущности — операторы, ветви или функции, — что позволяет предсказать количество тестов, которые будут выбраны при изменении этих сущностей. Существует как минимум одна прогнозирующая функ- ция, которая может быть использована для предсказания целесообразно- сти применения безопасной стратегии выборочного регрессионного тес- тирования. Пусть Р — тестируемая система, S — ее спецификация, Т — набор ре- грессионных тестов для Р, а | Т | означает число отдельных тестов в Т. Пусть М — выборочный метод регрессионного тестирования, используе- мый для отбора подмножества Т при тестировании измененной версии Р; М может зависеть от Р, S, Т, информации об исполнении Т на Р и дру- гих факторов. Через Е обозначим набор рассматриваемых М сущностей тестируемой системы. Предполагается, что Т и Е — непустые, и что каж- дый синтаксический элемент Р принадлежит, по крайней мере, одной сущности из Е. Отношение coversM(t, Е) определяется как отношение покрытия, достигаемого методом М для Р. Это отношение определено над ТхЕ и справедливо тогда и только тогда, когда выполнение теста t на Р приводит к выполнению сущности е как минимум один раз. Значение термина «выполнение» определено для всех типов сущностей Р. Напри- мер, если е — функция или модуль Р, е выполняется при вызове этой функции или модуля. Если е — простой оператор, условный оператор, пара определения-использования или другой вид элемента пути в графе выполнения Р, е выполняется при выполнении этого элемента пути. Ес- ли е — переменная Р, е выполняется при чтении или записи этой пере- менной. Если е — тип Р, е выполняется при выполнении любой перемен- ной типа е. Если е — макроопределение Р, е выполняется при выполне-
Лекция 13 Методики, не связанные с отбором тестов и методики порождения тестов нии расширения этого макроопределения. Если е — сектор Р, е выполня- ется при выполнении всех составляющих его операторов. Соответствую- щие значения термина «выполнение» могут быть определены по анало- гии для других типов сущностей Р. Для данной тестируемой системы Р, набора регрессионных тестов Т и выборочного метода регрессионного тестирования М можно предска- зать, стоит ли задействовать М для регрессионного тестирования будущих версий Р, используя информацию об отношении покрытия covers^, дос- тигаемого при использовании М для Т и Р. Прогноз основан на метрике стоимости, соответствующей Р и Т. Относительно издержек принимают- ся некоторые упрощающие предположения. Пусть Ес обозначает множество покрытых сущностей: Ес = {eGE | (3tGT)(coversM(t, Е))}. Обозначение |ЕС| используется для числа покрытых сущностей. Иногда удобно представить зависимость coversM(t, Е) в виде бинарной матрицы С, строки которой представляют элементы Т, а столбцы — эле- менты Е. При этом элемент Cjj матрицы С определяется следующим об- разом: ' 1, если coversM(i,j) О, иначе Степень накопленного покрытия, обеспечиваемого Т, то есть общее число единиц в матрице С, обозначается СС: |Т| |Е| СС = iSCij i=lj=l Отметим, что если ограничиться включением в С только столбцов, соответствующих покрытым сущностям Ес, накопленное покрытие СС останется неизменным. В частности, для всех непокрытых сущностей и Cj и равно нулю для всех тестов i (так как covers^C, и) ложно для всех та- ких случаев). Следовательно, ограничение на Ес при вычислении суммы, определяющей СС, приводит только к исключению слагаемых, равных нулю. Пусть Тм — подмножество Т, выбранное М для Р, и пусть |ТМ| обозна- чает его мощность, тогда Тм = {t£T | М выбирает t}. Пусть — удельная стоимость отбора одного теста для Тм при применении М к Р, и пусть г — удельная стоимость выполнения одного теста из Т на Р и проверки его ре-
зультата. М целесообразно использовать в качестве метода отбора тестов тогда и только тогда, когда: sM | Тм | < г(| Т | — | Тм |), то есть стоимость анализа, необходимого для отбора Тм, должна быть меньше стоимости прогона невыбранных тестов, Т2ТМ. Оценка ожидаемого числа тестов, требующих повторного запуска, обозначается NM и вычисляется следующим образом: Использование этой прогнозирующей функции предполагается только в случаях, когда цель выборочной стратегии регрессионного тес- тирования состоит в повторном выполнении всех тестов, затронутых из- менениями, то есть используется безопасный метод отбора тестов. Нес- колько усовершенствованный вариант оценки NM, использующий в ка- честве пространства сущностей Ес вместо Е: N с _ СС м Прогнозирующая функция для доли набора тестов, требующей по- вторного выполнения, то есть для |ТМ| / |Т|, обозначается лм: = СС I т I | ЕС || т I Прогнозирующая функция полагается непосредственно на информа- цию о покрытии. Главные предпосылки, лежащие в основе применения прогнозирующей функции, таковы: • Целесообразность применения выборочного метода регрессион- ного тестирования и, как следствие, наша способность к предска- занию целесообразности непосредственно зависит от доли тесто- вого набора, выбираемой для выполнения методом регрессион- ного тестирования. • Эта доля в свою очередь непосредственно зависит от отношения покрытия. Точность прогнозирующей функции на практике может значительно меняться от версии к версии. Проблема точности может оказаться доста- точно серьезной, тем не менее, поскольку прогнозирующая функция ис- пользуется для долговременного предсказания поведения метода на протя- жении нескольких версий, применение средних значений считается допу- стимым. Отношение coversM(t, е) в ходе сопровождения изменяется очень слабо. По этой причине информация, полученная в результате анализа
единственной версии, может оказаться достаточной для управления отбо- ром тестов на протяжении нескольких последовательных новых версий. Существуют факторы, влияющие на целесообразность отбора тес- тов, но не учитываемые прогнозирующей функцией. Один из подходов улучшения качества прогноза состоит в использовании информации об истории изменений программы, которую зачастую можно получить из системы управления конфигурацией. Например, в табл. 12.1 прогнозирующая функция может быть под- считана как отношение общего количества звездочек в таблице к количе- ству строк таблицы, т.е. числу покрываемых сущностей. Эта величина со- ставляет 42/11® 3.8, т.е. безопасный метод будет отбирать в среднем око- ло 4 тестов. Сведения о методике предсказания суммированы в табл. 13.4. Табл. 13.4. Результаты применения методики предсказания Характеристика Изменение в результате применения методики Время работы метода отбора в случае, если Увеличивается незначительно выборочное тестирование целесообразно Время работы метода отбора в случае, если Уменьшается до пренебрежи- выборочное тестирование нецелесообразно мо малой величины Снижение точности предсказания от вер- Зависит от объема изменений сии к версии Результаты применения методики на пра- Положительные (ошибка в ктике 0,8%) Порождение новых тестов Порождение новых тестов при структурном регрессионном тестиро- вании обычно обусловлено недостаточным уровнем покрытия. Новые тес- ты разрабатываются так, чтобы задействовать еще не покрытые участки ис- ходного кода. Процесс прекращается, когда уровень покрытия кода дости- гает требуемой величины (например, 80%). Разработка новых тестов при функциональном регрессионном тестировании является менее тривиаль- ной задачей и обычно связана с вводом новых требований либо с желани- ем проверить некоторые сценарии работы системы дополнительно. Основой большинства программных продуктов для управляющих применений, находящихся в промышленном использовании, является цикл обработки событий. Сценарий работы с системой, построенной по такой архитектуре, состоит из последовательности транзакций, управле- ние после обработки каждой транзакции вновь передается циклу обра-
ботки событий. Выполнение транзакции приводит к изменению состоя- ния программы; в результате некоторых транзакций происходит выход из цикла и завершение работы программы. Тесты для таких программ пред- ставляют собой последовательность транзакций. Развитие программного продукта от версии к версии влечет за собой появление новых состояний. Поскольку большинство тестов легко может быть расширено путем добавления дополнительных транзакций в список, новые тесты можно создавать путем суперпозиции уже имеющихся, с уче- том информации об изменении состояния тестируемой системы в резуль- тате прогона теста. Этот подход позволяет указать, какого рода новые те- сты с наибольшей вероятностью обнаружат ошибки. Обозначим тестируемую программу Р, а множество ее тестов Т = {Ц, t2,..., tn}. Будем считать, что состояние тестируемой программы s опреде- ляется совокупностью значений некоторого подмножества глобальных и локальных переменных. При создании новых тестов будем рассматривать состояния программы перед запуском теста (s0) и после его окончания (Sj). Информацию об этих состояниях необходимо собирать для каждого теста по результатам запуска на предыдущей версии продукта. Методика порождения новых тестов на основе анализа «подозрительных» состоя- ний сводится к описанной ниже последовательности действий. 1, Вычисление списка глобальных и локальных переменных, опре- деляющих состояние программы s. 2. Сбор информации (на основе анализа профиля программы, полу- ченного на предыдущей версии продукта i-1, для каждого сущест- вующего теста tj) о состояниях программы перед запуском теста и после его окончания (т.е. Sq и Sj). Множество таких состояний обо- значается Sj_!: Sj.j = Sq U {Sj | Vj}. 3. Выполнение на текущей версии продукта i новых и выбранных ре- грессионных тестов из множества Т' . По аналогии с Si_1 вычисля- ется множество Sj, которое сохраняется под управлением системы контроля версий. 4. Оценка «подозрительных» с точки зрения наличия ошибок мно- жества новых по сравнению с предыдущими версиями состояний Nj в соответствии со следующей формулой: Nj = Sj \ Sj.p 5. Анализ состояний множества Nj, в которых дальнейшая работа про- дукта невозможна в соответствии со спецификацией. Предмет ана- лиза — определить, создаются ли эти состояния в результате выпол- нения тестов, проверяющих нештатные режимы работы продукта, или каких-либо других тестов. В последнем случае фиксируется ошибка. 6. Исключение нештатных состояний из множества Nj.
7. Переход к шагу 10, если новых состояний, допускающих продол- жение выполнения программы, не обнаружено, т.е. Nj=0. 8. Для каждого состояния множества N, вычисление вектора отли- чия от исходного состояния Sq, т.е. множества переменных, изме- ненных по сравнению с s0. 9. Модификация множества измененных строк исходного кода DP на основе информации об измененных переменных и использова- ние какой-либо методики отбора тестов для выборочного регрес- сионного тестирования. 10. Повторное выполнение шагов 3-9 до достижения состояния Nj=0 либо до истечения времени, отведенного на регрессионное тестирование. Использование методов разбиения на классы экви- валентности для досрочного принятия решения о прекращении цикла тестирования, если ни один из тестов, созданных на оче- редном этапе, не принадлежит к новому классу эквивалентности. Для приведенной методики организации тестирования, когда новые тесты получаются в результате суперпозиции уже имеющихся, целесооб- разно в качестве исходных тестов, т.е. тех «кирпичиков», из которых будут строиться тесты в дальнейшем, брать тесты, заключающие всего одну эле- ментарную проверку. Это помогает избежать избыточности при много- кратном слиянии тестов, когда искомые «подозрительные» ситуации воз- никают в ходе работы теста, но не анализируются, так как не повторяют- ся при его завершении. Использование описанной методики позволяет в программном ком- плексе находить ошибки, не обнаруживаемые исходным набором тестов. Отметим, что применение предложенного подхода невозможно для про- грамм, понятие состояния для которых не определено.
Лекция 14. Регрессионное тестирование: алгоритм и программная система поддержки Рассматриваются методики регрессионного тестирования, полный ал- горитм регрессионного тестирования и программная система его поддержки. Ключевые слова: методика регрессионного тестирования, алгоритм регрессионного тестирования, система поддержки регрессионного тести- рования. Методика регрессионного тестирования Методика предназначена для эффективного решения задачи выбо- рочного повторного тестирования. Ее исходными данными являются: программа Р и ее модифицированная версия Р', критерий тестирования С, множество (набор) тестов Т, ранее использовавшихся для тестирования Р, информация о покрытии элементов Р (М(Р,С)) тестами из Т. Необходи- мо реализовать эффективный способ, гарантирующий достаточную сте- пень уверенности в правильности Р', используя тесты из Т. Методика строится на основе сочетания процедур обычного и рег- рессионного тестирования. Рассмотрим процедуру обычного тестирования. В ней для получе- ния информации о тестируемых объектах в ходе тестирования необходи- мо установить соответствие между покрываемыми элементами и тестами для их проверки. Соответственно, процедура тестирования должна вклю- чать приведенную ниже последовательность действий: I. Определить требуемые функциональные возможности программы с использованием, например, метода разбиения на классы эквива- лентности. 2. Создать тесты для требуемых функциональных возможностей. 3. Выполнить тесты. 4. В случае необходимости — создать и выполнить дополнительные тесты для покрытия оставшихся (еще не покрытых) структурных элементов (предварительно установив их соответствие функцио- нальным требованиям). 5. Создать базу данных тестов программы. По аналогии с обычным тестированием, процедура регрессионного тестирования в процессе сопровождения состоит из следующих этапов: 1. Использование функции предсказания целесообразности. Если прогнозируемое количество выбранных тестов больше, чем порог
целесообразности, провести повторный прогон всех тестов. В про- тивном случае перейти к шагу 2. 2. Идентификация изменений ДР в программе Р' (и множества ДМ измененных покрываемых элементов) и установление взаимно од- нозначного соответствия между покрываемыми элементами М(Р, С) и М(Р', С) в соответствии с изменениями: ДМ = (М(Р, С) \ М(Р', С)) U (М(Р', С) \ М(Р, С)) 3. Выбор Т' СТ — подмножества исходных тестов, потенциально спо- собных выявить связанные с изменениями ошибки в Р', для по- вторного выполнения на Р', с использованием результатов, полу- ченных в пункте 2. Это подмножество можно упорядочить, а также указать число тестов, выполнения которых достаточно для соответ- ствия какому-либо критерию минимизации. Для безопасных мето- дов отбора тестов множество Т' удовлетворяет следующим ограни- чениям: tjGT, tj^T' => P(tj) s Р' (tj) 4. Применение подмножества Т' для регрессионного тестирования измененной программы Р' с целью проверки результатов и уста- новления факта корректности Р' по отношению к Т' (в соответ- ствии с измененным техническим заданием), а также обновления информации о прохождении тестов из Т' на Р'. 5. В случае необходимости — создание дополнительных тестов для дополнения набора регрессионных тестов. Это могут быть новые функциональные тесты, необходимые для тестирования измене- ний в техническом задании или новых функциональных возмож- ностей измененной программы; новые структурные тесты для ак- тивизации оставшихся (непокрытых) структурных элементов (предварительно установив их соответствие проверяемым функ- циональным требованиям). 6. Создание Т " — нового набора тестов для Р', применение его для тестирования измененной программы, проверка результатов и ус- тановление факта корректности Р' по отношению к Т", обновле- ние информации о ходе исполнения теста и создание базы данных тестов измененной программы для хранения этой информации и выходных данных тестов. Удаление устаревших тестов. Т " форми- руется по следующему правилу: Т" = (Т и Т )\Т 1 v * 1 новые/ ' 1 устаревшие- Система поддержки регрессионного тестирования Структура системы поддержки регрессионного тестирования пред- ставлена на рис. 14.1. Исходный код обеих версий тестируемой програм-
Курс Основы тестирования программного обеспечения Состояние изменилось Состояние не изменилось Расширение множества Т’ за счет тестов, проверяющих новое состояние программы Множество регрессионных тестов ....... .--МШИ.............Г» Рис. 14.1. Структура системы поддержки регрессионного тестиро- вания мы хранится под управлением системы контроля версий. Для отбора тес- тов по методу покрытия точек использования неисполняемых определе- ний средствами системы контроля версий создается файл различий, на основании которого вычисляется список добавленных, измененных и удаленных строк исходного кода, который является удобной формой представления множества ДР. Затем производится перебор этого списка. Если какая-либо строка списка представляет собой макроопределение,
осуществляется поиск строк кода, содержащих использование этого мак- роопределения по всему тексту тестируемой программы; найденные стро- ки присоединяются к множеству ДЕ Расширенное множество ДР сопос- тавляется с результатами прогона тестов из множества Т на предыдущей версии программы. Если в ходе выполнения какого-либо теста tj получа- ла управление хотя бы одна строка, входящая в множество ДР, тест tj от- бирается для повторного запуска. При создании новых тестов по методу «подозрительных» состояний функция тестируемой программы, содержащая цикл обработки событий, дополняется операторами вывода значений глобальных и видимых ло- кальных переменных. Запуск тестов из множества Т' на профилирован- ной версии программы позволяет получить список ее состояний. Этот список анализируется, и для каждого ранее не наблюдавшегося состоя- ния вычисляется список переменных, изменившихся по сравнению с ка- ким-либо известным состоянием. Множество ДР дополняется строками кода, где используются переменные из этого списка. Для каждого состо- яния указываются тесты, запуск которых необходим. Наконец, создается список рекомендованных новых тестов в форме, удобной для восприятия человеком. Выходные данные каждой программы-обработчика доступны поль- зователю, что позволяет контролировать промежуточные результаты ра- боты системы. К примеру, можно исключить из рассмотрения перемен- ные, которые хотя и изменяются в ходе выполнения программы, на ее состояние не влияют. Архитектура системы позволяет легко расширять функциональность: например, для поддержания какой-либо новой сис- темы контроля версий достаточно создать один новый модуль объемом около 100 строк кода. Остальные модули можйо использовать без изме- нений. Типовой сценарий проведения регрессионного тестирования про- грамм, написанных на языке С, с применением описанной выше системы состоит из следующих этапов: 1. Вычисляется множество АР строк исходного кода, добавленных, удаленных или измененных по сравнению с предыдущей версией. 2. Множество АР дополняется строками, непосредственно не изме- нявшимися, но содержащими ссылки на измененные макроопре- деления. 3. Вычисляется упорядоченное множество регрессионных тестов Т', для которых ViGT' Tj j-i^AP # 0, где Т, j_| — множество строк ис- ходного кода продукта, получающих управление в ходе выполне- ния теста i на версии системы j-1. Тесты упорядочиваются по убы- ванию количества измененных строк в пути их исполнения.
4. Вычисляется список глобальных и локальных переменных, опре- деляющих состояние программы s. Исходный код тестируемой программы модифицируется так, что информация о состоянии s (значения глобальных, статических и локальных переменных) вы- водится во внешний файл перед запуском теста и после его окон- чания. 5. Новые тесты и тесты из множества Т' (регрессионные тесты) ис- полняются на текущей версии продукта]. 6. Тесты, проверяющие нештатные режимы работы продукта, т.е. со- здающие состояния, в которых дальнейшая работа продукта невозможна в соответствии со спецификацией требований, ис- ключаются из рассмотрения. Если известно, что ни один тест не приводит к возникновению нештатных состояний, данный этап может быть опущен. 7. Для каждого теста i вычисляется множество Тц. 8. Обрабатываются результаты выполнения тестов, и создается мно- жество Sj, состоящее из начального состояния s0 и всех наблюдав- шихся конечных состояний. Вычисляется множество Nj= Sj\Sj_j всех новых по сравнению с предыдущими версиями состояний, которое является «подозрительным» с точки зрения наличия оши- бок, и вектора их отличий от исходного состояния s0, т.е. перемен- ные, измененные по сравнению с s0. 9. Множество измененных строк исходного кода ДР дополняется но- мерами строк, где используются заданные переменные. 10. Если Nj = 0, цикл работы завершается. Если Nj * 0, следует пе- рейти к шагу 4 или, если счетчик количества итераций работы си- стемы превышает некоторое заданное предельное значение, из- вестить об этом пользователя. Пользователь может принять ре- шение о прекращении тестирования или пропуске некоторого числа циклов. Если этап 6 выполняется автоматически, а этап 7 можно опустить, возможна полная автоматизация регрессионного тестирования.
Практикум по тестированию программного обеспечения

Введение В настоящее время очень актуальной стала проблема подготовки тес- тировщиков программного обеспечения. Настоящий практикум предна- значен для того, чтобы сформировать у студентов навыки тестирования программного обеспечения на базе применения теории и современных методов тестирования к практическим приложениям. Обычно это доста- точно легко сделать для простых учебных примеров, для которых смысл проблематики тестирования не ощущается, так как с небольшими прило- жениями и так все ясно, однако когда дело доходит до тестирования реаль- ных систем, оказывается, что полученных знаний недостаточно. Необхо- дим опыт работы с большой системой, когда объем проверяемого кода, сложность и детальность требований, а также ответственность за результат приводят к осознанию профессиональных условий работы тестировщика. Именно поэтому в качестве тестируемой системы была выбрана си- стема с очевидной функциональностью, но с далеко не простой системой требований и критериев корректности — модель реальной системы упра- вления автоматизированным комплексом хранения подшипников. Ин- формация о функционировании выбранной системы представлена при- мерно тем же способом и в виде тех же документов, с какими сталкивает- ся тестировщик по окончании фазы дизайна и в начале фазы разработки тестов — это «Спецификация требований» (Requirement Specification — Приложение 2) и «Высокоуровневый дизайн» (High Level Design — Приложение 3). На этой основе изучаются и самостоятельно разрабаты- ваются тесты (test case), тестовые наборы (test suite) и программы тестиро- вания, использующиеся как при ручном, так и при автоматизированном подходе к тестированию. Применение различных подходов к тестированию прикладной сис- темы демонстрируется на базе готовых и подробно разобранных приме- ров. Это позволяет самостоятельно выполнять задания по аналогии и бы- стро усваивать материал. Такой подход с методической точки зрения дос- таточно удачен и обоснован. Материал практикума изучается в следующем порядке: 1. Начинаем с изучения функциональности тестируемой системы и ее окружения по краткому описанию раздела 1 и документам приложе- ния: «Спецификации требований» и «Высокоуровневому дизайну». Изучаем факторы, влияющие на планирование деятельности тести- ровщика. Изучаем особенности модульного тестирования объектно-ориенти- рованной программы на примере тестирования классов. Освоение материала контролируется выполнением самостоятельно- го задания № 1.
2. Изучаем особенности интеграционного тестирования — способы описания тестов и тестовых процедур для программного комплекса, собранного из модульно оттестированных компонентов. Освоение материала контролируется выполнением самостоятельно- го задания №2. 3. Изучаем особенности процесса системного тестирования. Освоение материала контролируется выполнением самостоятельно- го задания №3. 4. Изучаем методику ручной разработки тестов. Освоение материала контролируется выполнением самостоятельно- го задания №4. 5. Изучаем методику автоматизации разработки и прогона тестов на основе скриптов. Освоение материала контролируется выполнением самостоятельно- го задания №5. 6. Изучаем методику генерации и прогона тестов по формальным опи- саниям языка Message Sequence Chart (MSC). Освоение материала контролируется выполнением самостоятельно- го задания №6. Изучение различных подходов к тестированию, рассматриваемое на основе одних и тех же тестовых сценариев, дает возможность сравнить их трудоемкость и понять необходимость автоматизации тестирования, как для разработки, так и для прогона тестовых наборов. Авторы выражают искреннюю благодарность Московскому отделе- нию Microsoft Corporation, спонсировавшему разработку настоящего по- собия, и лично Люцареву В.С., отметившему своевременность и полез- ность данной работы. Предлагаемый вашему вниманию читателей практикум обобщает опыт многолетней работы учебного центра «Политехник — Моторола» в Санкт-Петербургском государственном политехническом университете. Разработка практикума была бы невозможна без самоотверженной работы студентов и аспирантов, выполнивших разработку инструмен- тального средства автоматизации тестирования и проверку всех приме- ров. Коллектив в составе студентов 4 курса К. Кудряшева, Д. Пескова, М. Даишева, Е. Марченкова и аспирантов Д. Югая и В. Сухомлинова был организован в виде программистской бригады и вел разработку по зако- нам создания промышленного проекта.
Семинар 1. Описание тестируемой системы и ее окружения. Планирование тестирования Практикум базируется на тестировании модели реальной системы управления автоматизированным комплексом хранения подшипников. Она обеспечивает прием подшипников на склад, сохранение характери- стик поступивших подшипников в базе данных (БД), а при поступлении заявки на подшипники вместе с параметрами оси — подбор подходящих подшипников и их выдачу. У каждого из элементов комплекса (склада, терминала подшипника и терминала оси) существует программа низко- уровневого управления, реализованная в виде динамически подключае- мой библиотеки (dll), принимающая на вход высокоуровневые команды, и преобразующая их в управляющие воздействия на данный элемент тес- тируемой системы. Таким образом, есть реальное окружение — аппарату- ра и dll, которые осуществляют связь с аппаратурой. Система вызывает следующие функции из dll для своих элементов (см. рис. 1.1): GetStoreStat, GetStoreMessage, SendStoreCom (Store.dll) для склада. GetAxlePar (Axle.dll) для терминала оси. GetRollerPar (Bearing.dll) для терминала подшипника. Управляет переферийными Рис. 1.1. Система и ее окружение
Поскольку для тестирования используется модель системы, в ее со- ставе реальное окружение заменено на модельное, обеспечиваемое спе- циальной библиотекой dll-функций окружения. Тестируемая система реализована как многопоточное приложение. Многопоточность порождает недетерминированность поведения систе- мы во времени. Поэтому при тестировании необходимо учитывать, что возможны различные варианты допустимых временных последователь- ностей событий системы. Кроме того, совсем не просто точно воспроиз- вести прогон конкретного теста, когда система содержит много парал- лельных потоков, так как планировщик операционной системы сам опре- деляет порядок событий. Изменения, вносимые в программы, не связан- ные с тестируемой системой, могут повлиять на порядок, в котором будут выполняться (воспроизводиться) потоки событий тестируемой системы. Это может привести к тому, что после выявления и исправления дефекта при проведении повторного тестирования далеко не всегда удастся убе- диться в том, что дефект действительно устранен, если ошибка не обнару- жена во время прогона. При системном тестировании мы рассматриваем систему как чер- ный ящик. Тестовый случай (test case) представляет собой пару (входные данные, ожидаемый результат), в которой входные данные — это описа- ние данных, подаваемых на вход нашей системы, а ожидаемый результат — это описание выходных данных, которые система должна предъявить в ответ на соответствующий ввод. Выполнение (прогон) тестового случая — это сеанс работы системы, в рамках которого на вход системы подаются наборы данных, предусмотренные спецификацией тестового случая, и фиксируются результаты их обработки, которые затем сравниваются с ожидаемыми результатами, указанными в тестовом случае. Если факти- ческий результат отличается от ожидаемого, значит, обнаружен отказ, т.е. тестируемая система не прошла испытание на заданном тестовом случае. Если полученный результат совпадает с ожидаемым, значит, тестируемая система прошла испытание на заданном тестовом случае. Из тестовых случаев формируются тестовые наборы (test suits). Тестовые наборы орга- низованы в определенном порядке, отражающем свойства тестовых слу- чаев. Если система успешно справилась со всеми тестовыми случаями из набора, то она успешно прошла испытания на тестовом наборе. Для нашей системы входными данными является состояние окруже- ния ее компонентов: Склад. Состояние склада будет характеризоваться следующими па- раметрами: Статус склада (StoreStat). Сообщение от склада о результатах выполнения команды (Store Message).
Сообщение от склада о результатах получения команды — статус ко- манды (CommandStatus). Терминал подшипника. Состояние терминала подшипника задается следующими параметрами: Статус обмена с терминалом подшипника. Характеристики (параметры) подшипника (RollerPar)-. ФИО мастера, производившего измерения. Название депо. Номер рабочей смены. Номер подшипника. Номер группы подшипника. Тип сепаратора подшипника. Терминал оси. Состояние терминала оси задается следующими пара- метрами: Статус обмена с терминалом оси. Характеристики (параметры) оси (AxlePar)-. ФИО мастера, производившего измерения. Название депо. Номер оси. Сторона оси: правая или левая. Посадочный диаметр задний. Посадочный диаметр передний. База данных (БД). В БД хранятся характеристики поступивших на склад подшипников. При выборе подходящего для оси подшипника система обращается за этой информацией к БД. Поэтому имеет смысл предварительно очистить БД или заполнить ее определенны- ми данными. В спецификации тестового случая должны быть заданы состояние окружения (входные данные) и ожидаемая последовательность событий в системе (ожидаемый результат). После прогона тестового случая мы по- лучим реальную последовательность событий в системе (выходные дан- ные) при заданном состоянии окружения. Сравнивая фактический ре- зультат и ожидаемый, можно сделать вывод о том, прошла ли тестируемая система испытание на заданном тестовом случае. В качестве ожидаемого результата будем использовать пошаговое описание случая использования (use case), так как оно определяет, как при заданном состоянии окружения система должна функционировать. Задавая ожидаемый результат, очень важно помнить о том, что при заданном состоянии окружения возможны различные варианты последовательности событий системы, которые все являются правильными.
В процессе работы последовательность событий (команд) системы, или история системы, записывается в журнал (log) системы. Вы можете использовать SystemLogAnimator (см. п.14 SysLog Animator Manual) для визуализации журнала системы. Выбирая различные log-файлы системы для визуализации, можно получить наглядное и достаточно полное пред- ставление о функционировании системы и о том, какие события и в ка- ком порядке могут происходить в системе. Планирование тестирования Процесс тестирования Процесс тестирования находится в прямой зависимости от процесса разработки программного обеспечения, но при этом сильно отличается от него, поскольку преследует другие цели. Разработка ориентирована на построение программного продукта, тогда как тестирование отвечает на вопрос, соответствует ли разрабатываемый программный продукт требо- ваниям, в которых зафиксирован первоначальный замысел изделия (т.е. то, что заказал заказчик). Вместе оба процесса охватывают виды деятельности, необходимые для получения качественного продукта. Ошибки могут быть привнесены на каждой стадии разработки. Следовательно, каждому этапу разработки должен соответствовать этап тестирования. Отношения между этими процессами таковы, что если что-то разрабатывается, то оно подвергает- ся тестированию, а результаты тестирования используются для определе- ния, соответствует ли это «что-то» набору предъявляемых требований. Процесс тестирования возвращает выявленные им ошибки в процесс раз- работки. Процесс разработки передает процессу тестирования новые и исправленные проектные версии. Планирование тестирования Как было отмечено выше, процесс тестирования тесно связан с про- цессом разработки. Соответственно планирование тестирования тоже за- висит от выбранной модели разработки. Однако вне зависимости от мо- дели разработки при планировании тестирования необходимо ответить на пять вопросов, определяющих этот процесс: Кто будет тестировать и на каких этапах? Разработчики продукта, независимая группа тестировщиков или со- вместно?
Ш--- - ... ..— 1 1 _ Какие компоненты надо тестировать? Будут ли подвергнуты тестированию все компоненты программного продукта или только компоненты, которые угрожают наибольшими поте- рями для всего проекта? Когда надо тестировать? Будет ли это непрерывный процесс, вид деятельности, выполняе- мый в специальных контрольных точках, или вид деятельности, выпол- няемый на завершающей стадии разработки? Как надо тестировать? Будет ли тестирование сосредоточено только на проверке того, что данный продукт должен выполнять, или также на том, как это реализовано? В каком объеме тестировать? Как определить, в достаточном ли объеме выполнено тестирование, или как распределить ограниченные ресурсы, выделенные под тестирова- ние? Кто будет тестировать? Разработчик — это роль, для которой характерны виды деятельности, ориентированные на создание программного продукта (ПП). Тестировщик — это роль, для которой характерны виды деятельности, ориентированные на обнаружение ошибок в ПП. Эта роль предусматрива- ет выбор тестов, необходимых для конкретных целей, построение тестов, выполнение тестов и оценку результатов. Конкретный исполнитель про- екта может выступать как в роли разработчика, так и в роли тестировщи- ка. Момент начала тестирования в проекте можно регулировать (рис. 1.2). Разработчики Разработчики и тестировщики Тестировщики Рис. 1.2. Кто тестирует В рамках данного практикума студенту предназначена роль тести- ровщика. Какие компоненты надо тестировать? Могут быть варианты, когда ничего не надо тестировать (поскольку все компоненты были протестированы ранее), а может потребоваться те- стировать каждый компонент с точностью до строки кода. В объектно- ориентированном программировании базовым компонентом является
класс. В этом случае область тестирования определяется классами. Об- ласть тестирования на уровне классов подлежит выбору (рис. 1.3). Клас- сы, заимствованные из других проектов или взятые из библиотек, чаще всего в повторном тестировании не нуждаются. Существуют различные стратегии по выбору подмножества классов для тестирования. Ничего не тестировать Тестировать подмножество компонентов Тестировать все Рис. 1.3. Что тестировать В нашем случае будет производиться тестирование всех классов при- ложения. Когда надо тестировать? Компоненты можно тестировать на завершающем этапе, когда они будут интегрированы в единый выполняемый модуль. Частота тестирова- ния определяется различными соображениями. Можно проводить тести- рование каждый день, учитывая тот факт, что чем раньше выявлена про- блема, тем легче и дешевле ее решение. Можно тестировать программный компонент по мере завершения его разработки (рис. 1.4). Частое тестиро- вание компонентов несколько замедляет ранние этапы разработки, одна- ко сопряженные с этим потери с лихвой восполняются за счет меньшего числа проблем на более поздних этапах разработки проекта, когда отдель- ные модули объединяются в более крупные компоненты системы. В случае, когда компоненты системы не отличаются большой слож- ностью, можно сначала осуществлять интегрирование не подвергавшихся автономному тестированию компонентов, а затем тестировать объединен- ный код как единое целое. Такой подход полезен при тестировании ком- понентов, для которых реализация тестовых драйверов требует существен- ных усилий. Тестовый драйвер представляет собой программу, которая вы- полняет прогон тестовых случаев и сбор полученных при этом результатов. Выполнять ежедневное тестирование Тестировать компоненты по мере их готовности Тестировать все компоненты на завершающем этапе %, j Рис. 1.4. Когда тестировать Студентам предлагается тестировать компоненты по мере их готов- ности.
Как надо тестировать? Основные подходы к тестированию ПО основаны на спецификации и реализации (рис. 1.5). Спецификация модуля (или класса) ПП определяет, что этот модуль должен делать, т.е. она описывает допустимые наборы входных данных, подаваемых на вход модуля, включая ограничения на то, как многократ- ные вводы данных должны соотноситься друг с другом, и какие выходные данные соответствуют различным наборам входных данных. Реализация модуля ПП есть выражение алгоритма, порождающего выходные результаты для различных наборов входных данных с соблюде- нием требований спецификации. Спецификация указывает, что делает мо- дуль ПП, а реализация показывает, как модуль ПП это делает. Полный учет требований спецификации дает гарантию того, что ПП выполняет все, что от него требуется. Полный учет требований к реализации дает га- рантию того, что ПП не будет делать того, что от него не требуется. Спецификация играет важную роль в тестировании. Обычно для мно- жества компонентов ПП создаются спецификации, обеспечивающие разра- ботку и тестирование, включая спецификации систем, подсистем и классов. Наряду с автономным тестированием компонентов (классов) системы (модульным уровнем тестирования), необходимо тестировать взаимодейст- вие между различными компонентами (интеграционный уровень тестирова- ния). Цель интеграционного тестирования заключается в обнаружении отка- зов, возникающих вследствие ошибок в интерфейсах или в силу неверных предположений относительно интерфейсов. После интеграционного тести- рования проводится системное тестирование ПП (системный уровень). На этом уровне тестированию подвергается система как единое целое. Тестирование следует осуществлять в достаточных объемах, чтобы быть более-менее уверенным в том, что ПП функционирует в соответствии с предъявленными к нему требованиями, т.е. выполняется принцип адек- ватности тестирования ПП. Адекватность можно измерить, используя по- нятие покрытия. Покрытие можно измерить двумя способами. Первый за- ключается в подсчете количества требований, сформулированных в специ- фикации, которые подверглись тестированию. Второй способ заключается в подсчете выполненных компонентов ПП в результате прогона тестового набора. Набор тестов можно считать адекватным, если определенная часть строк исходного кода или исполняемых ветвей исходного кода была вы- полнена, по крайней мере, один раз во время прогона тестового набора. Эти два способа измерения отражают два базовых подхода к тестированию: • при использовании первого подхода проверяется, что должен вы- полнять ПП; • при использовании второго подхода проверяется, как фактически работает ПП.
При тестировании в соответствии со спецификацией (функциональ- ном тестировании или тестировании «черного ящика») построение тесто- вых случаев производится в соответствии со спецификацией и не зависит от того, как реализован ПП. Эффективность зависит от качества специ- фикации и способности тестировщика корректно ее интерпретировать. При структурном тестировании (тестировании в соответствии с ре- ализацией или тестировании «белого ящика») построение тестовых случа- ев производится на основе программного кода, представляющего собой реализацию ПП. Входные данные каждого тестового случая должны быть определены спецификацией ПП, однако они могут быть выбраны на ос- нове анализа самого программного кода для прохождения той или иной ветви программы. При этом покрытие увеличивается. Знание только спецификации Знание спецификации и реализации Рис. 1.5. Как тестировать Мы будем использовать оба подхода. При тестировании классов мы будем стремиться покрыть как спецификации классов, так и код их реа- лизации. При тестировании взаимодействий будем покрывать специфи- кацию. При системном тестировании также будем стремиться покрыть спецификацию системы. В каком объеме тестировать? Различные уровни адекватного тестирования изображены на рис. 3.5, который охватывает случаи от отсутствия тестирования до исчерпываю- щего тестирования, когда выполняется прогон всех возможных тестовых случаев. Объем необходимого тестирования следует определять исходя из краткосрочных и долгосрочных целей проекта и в соответствии с особен- ностями разрабатываемого ПП. Покрытие — это мера полноты использо- вания возможностей программного компонента тестовым набором. Например, одна из мер — задействована ли каждая строка программно- го кода продукта хотя бы один раз при прогоне данного тестового набора. Другая мера — количество требований спецификации, проверенных данным тестовым набором. Если требования сформулированы в терминах случаев ис- пользования, то покрытие измеряется количеством случаев использования и числом сценариев, построенных для каждого случая использования. Анализ рисков в процессе тестирования применяется для определе- ния уровня детализации и времени, затрачиваемого на тестирование кон- кретного компонента. Например, на тестирование классов, более важных для приложения, отводится больше времени.
Тестирование не выполняется Исчерпывающее тестирование Рис. 1.6. В каком объеме тестировать Мы будем использовать все перечисленные меры. Ответы на поставленные вопросы и, возможно, на многие другие, оформляются в виде набора документов, принятого в компании. Напри- мер, тестовый план может содержать следующую информацию: 1. Перечень тестовых ресурсов. 2. Перечень функций и подсистем, подлежащих тестированию. 3. Тестовую стратегию: • Анализ функций и подсистем с целью определения слабых мест, требующих исчерпывающего тестирования, то есть уча- стков функциональности, где появление дефектов наиболее вероятно. • Определение стратегии выбора входных данных для тестиро- вания. Поскольку в реальных применениях множество вход- ных данных программного продукта практически бесконеч- но, выбор конечного подмножества для проведения тестиро- вания является сложной задачей. Для ее решения могут быть применены методы покрытия классов входных и выходных данных, анализ крайних значений, покрытие случаев исполь- зования и тому подобное. Выбранная стратегия должна быть обоснована и задокументирована. • Определение потребности автоматизации процесса тестиро- вания. При этом решение об исподьзовании существующей, либо о создании новой автоматизированной системы тести- рования должно быть обосновано, а также продемонстриро- вана оценка затрат на создание новой системы или на внедре- ние уже существующей. 4. График (расписание) тестовых циклов. 5. Указание конкретных параметров аппаратуры и программного ок- ружения. 6. Определение тестовых метрик, которые необходимо собирать и анализировать, таких как покрытие набора требований, покрытие кода, количество и уровень серьезности дефектов, объем тестового кода и т.п.
Семинар 2. Модульное тестирование на примере классов • Цель тестирования программных модулей состоит в том, чтобы удо- стовериться, что каждый модуль соответствует своей спецификации. Ес- ли это так, то причиной любых ошибок, которые возникают при их объе- динении, является неправильная стыковка модулей. В процедурно-ори- ентированном программировании модулем называется процедура или функция, иногда группа процедур, которая реализует абстрактный тип данных. Тестирование модулей обычно представляет собой некоторое со- четание проверок и прогонов тестовых случаев. Можно составить план те- стирования модуля, в котором учесть тестовые случаи и построение тес- тового драйвера. Тестирование классов аналогично тестированию модулей. Основ- ным элементом объектно-ориентированной программы является класс. Рассмотрим методику тестирования отдельного класса. Тестирование классов охватывает виды деятельности, ассоциированные с проверкой реализации класса на точное соответствие спецификации класса. Если реализация корректна, то каждый экземпляр этого класса ведет себя по- добающим образом. Эффективного тестирования классов можно достичь при помощи ревью и тестовых прогонов. Ревью представляет собой просмотр исход- ного кода ПО с целью обнаружения ошибок и дефектов, возможно, до того, как это ПО заработает. Ревьюирование предназначено для выявле- ния таких ошибок, как неспособность выполнять то или иное требова- ние спецификации или ее неправильное понимание, а также алгоритми- ческих ошибок в реализации. Тестовый прогон обеспечивает тестирова- ние ПО в процессе выполнения программы. Осуществляя прогон про- граммы, тестировщик стремится определить, способна ли программа вести себя в соответствии со спецификацией. Тестировщик должен вы- брать наборы входных данных, определить соответствующие им пра- вильные наборы выходных данных и сопоставить их с реально получае- мыми выходными данными. Рассмотрим тестирование классов в режиме прогона тестовых слу- чаев. После идентификации тестовых случаев для класса нужно реали- зовать тестовый драйвер, обеспечивающий прогон каждого тестового случая, и запротоколировать результаты каждого прогона. При тести- ровании классов тестовый драйвер создает один или большее число эк- земпляров тестируемого класса и осуществляет прогон тестовых случа- ев. Тестовый драйвер может быть реализован как автономный тестиру- ющий класс.
Кто, что, когда, как и в каком объеме? Рассмотрим эти вопросы в контексте тестирования классов. Кто выполняет тестирование?Обычно тестирование классов выпол- няют их разработчики. В этом случае время на изучение спецификации и реализации сводится к минимуму. Недостатком подхода является то, что если разработчик неправильно понял спецификации, то он для своей неправильной реализации разработает и «ошибочные» тестовые наборы. Что тестировать? Необходимо удостовериться, что программный код класса в точности отвечает требованиям, сформулированным в его спецификации, и что он не делает ничего более. В какой момент следует выполнять тестирование? План тестирова- ния или хотя бы тестовые случаи должны разрабатываться после состав- ления полной спецификации класса. Разработка тестовых случаев по ме- ре реализации класса помогает разработчику лучше понять специфика- цию. Тестирование класса должно проводиться до того, как возникнет необходимость использовать этот класс в других компонентах ПО. Рег- рессионное тестирование класса должно выполняться всякий раз, когда меняется реализация класса. Регрессионное тестирование позволяет убе- диться в том, что разработанные и оттестированные функции продолжа- ют удовлетворять спецификации после выполнения модификации ПО. Как будет выполняться тестирование.? Тестирование классов обычно выполняется путем разработки тестового драйвера, который создает эк- земпляры классов и окружает эти экземпляры соответствующей средой (тестовым окружением), чтобы стал возможен прогон соответствующего тестового случая. Драйвер посылает сообщения экземпляру класса в соот- ветствии со спецификацией тестового случая,, а затем проверяет исход этих сообщений. Тестовый драйвер должен удалять созданные им экзем- пляры тестируемого класса. Статические элементы данных класса также необходимо тестировать. Какие объемы тестирования следует считать адекватными?Адекват- ность может быть измерена полнотой охвата тестами спецификации или реализации. Будем использовать оба способа. Что тестировать? Можно выделить два типа классов с точки зрения их взаимодействия с другими классами: • примитивные классы; • непримитивные классы. Примитивный класс может порождать экземпляры, и эти экземпляры можно использовать без необходимости создания экземпляров каких-либо других классов, в том числе и данного класса. Такие объекты представляют
собой простейшие компоненты системы и, несомненно, играют важную роль при выполнении любой программы. Тем не менее, в объектно-ориенти- рованной программе существует сравнительно небольшое количество при- митивных классов, которые реалистично моделируют объекты задачи и все отношения между этими объектами. Обычным явлением для хорошо спрое- ктированных объектно-ориентированных программ является использова- ние непримитивных классов. Основываясь на этой информации, определим, к какому типу относится каждый класс в нашем приложении (табл. 2.1). Табл. 2.1. Типы Классов Класс Тип TBearingParam Примитивный TAxleParam Примитивный TCommand Примитивный TLog Примитивный TCommandQueue Непримитивный TStore Непримитивный TTerminalBearing Непримитивный TTerminalAxle Непримитивный TModel Непримитивный MainForm Непримитивный В большинстве объектно-ориентированных языков члены класса имеют один из трех уровней доступа: Public. Члены с доступом public доступны из любых классов. Они об- разуют интерфейс класса, которым будет пользоваться любой разра- ботчик, использующий данный класс в своем приложении. Private. Члены с доступом private доступны только внутри самого класса, то есть из его методов. Они являются частью внутренней ре- ализации класса и недоступны стороннему разработчику. Protected. Члены с доступом protected доступны из самого класса и из классов, являющихся его потомками, но недоступны извне. Исполь- зование этих методов возможно только при создании класса-потом- ка, расширяющего функциональность базового класса. Таким образом, необходимость тестирования функциональности класса зависит от того, предоставляется ли им возможность наследова- ния. Если класс является законченным (final) и не предполагает наследо- вания, необходимо тестирование его public части (впрочем, классы final
не содержат protected членов). Если же класс рассчитан на расширение за счет наследования, необходимо тестирование также его protected части. Кроме того, во многих языках класс может содержать статические (static) члены, которые принадлежат классу в целом, а не его конкретным экземплярам. При наличии public static или protected static членов, кроме тестирования объектов класса, должно отдельно выполняться тестирова- ние статической части класса. Как тестировать? Как уже упоминалось, для тестирования классов применяются тес- товые драйверы. Существует несколько способов реализации тестового драйвера: Тестовый драйвер реализуется в виде отдельного класса. Методы это- го класса создают объекты тестируемого класса и вызывают их мето- ды, в том числе статические методы класса. Таким способом можно тестировать public часть класса. Тестовый драйвер реализуется в виде класса, наследуемого от тести- руемого. В отличие от предыдущего способа, такому тестовому драй- веру доступна не только public, но и protected часть. Тестовый драйвер реализуется непосредственно внутри тестируемого класса (в класс добавляются диагностические методы). Такой тесто- вый драйвер имеет доступ ко всей реализации класса, включая pri- vate члены. В этом случае в методы класса включаются вызовы отла- дочных функций и агенты, отслеживающие некоторые события при тестировании. В дальнейшем мы будем использовать первый способ при реализа- ции драйверов. При разработке спецификации класса можно задействовать один из следующих подходов: Контрактный подход. Интерфейс определяется в виде обязательств отправителя и получателя, вступивших во взаимодействие. Опера- ция определяется как набор обязательств каждой стороны, причем ответственность по отношению друг к другу соблюдается как отпра- вителем, так и получателем. Подход защитного программирования. Интерфейс определяется глав- ным образом в терминах получателя. Операция возвращает результат запроса — успешное или неудачное выполнение по конкретной при- чине (например, по недопустимому входному значению). Другими словами, соответствующий получатель следит за тем, чтобы на вход не попали некорректные данные, т.е. проверяет правильность и до- пустимость входных данных, и после получения запроса сообщает отправителю результат обработки запроса.
Различие между контрактным и защитным методами проектирования распространяется и на тестирование. Контрактное проектирование возла- гает большую ответственность на проектировщика, чем на программы по- иска ошибок. Основное внимание во время тестирования взаимодействий в условиях контактного подхода уделяется проверке того, выполнены ли объектом-отправителем предусловия методов получающего объекта. Не допускается построение тестовых случаев, нарушающих эти предусловия. Обычно практикуется перевод объекта-получателя в некоторое заданное состояние, после чего инициируется выполнение тестового драйвера, по условиям которого объект-отправитель требует, чтобы объект-получатель находился в другом состоянии. Смысл подобной проверки заключается в том, чтобы установить, выполняет ли объект-отправитель проверку преду- словий объекта-получателя, прежде чем отправить заранее неприемлемое сообщение, и корректно ли он прекращает свою работу. Подробное описание тестового случая Рассматривается пример тестов на C# для класса TCommand (приложе- ние 3 (HLD)). При выполнении заданий необходимо будет самостоятельно написать тесты для других классов приложения. Параллельно с изучением этого раздела полезно открыть проект ModuleTesting\ModuleTests.sln. Рассмотрим тестирование класса TCommand. Этот класс реализует единственную операцию GetFullName (), которая возвращает полное на- звание команды в виде строки. Разработаем спецификацию тестового слу- чая для тестирования метода GetFullName на основе спецификации этого класса (приложение 2): Название класса: TCommand Название тестового случая: TCommandTest1 Описание тестового случая: Тест проверяет правильность работы ме- тода GetFullName — получения полного названия команды на основе кода команды. В тесте подаются следующие значения кодов команд (входные значения): -1, 1, 2, 4, 6, 20, где -1 — запрещенное значение
Начальные условия: Нет Ожидаемый результат: Перечисленным входным значениям должны соответствовать сле- дующие выходные: Коду команды -1 должно соответствовать сообщение «ОШИБКА: Неверный код команды» Коду команды 1 должно соответствовать полное название команды «ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ» Коду команды 2 должно соответствовать полное название команды «ОТПРАВИТЬ ИЗ ЯЧЕЙКИ В ВЫХОДНУЮ ЯЧЕЙКУ» Коду команды 4 должно соответствовать полное название команды «ПОЛОЖИТЬ В РЕЗЕРВ» Коду команды 6 должно соответствовать полное название команды «ПРОИЗВЕСТИ ЗАНУЛЕНИЕ» Коду команды 20 должно соответствовать полное название команды «ЗАВЕРШЕНИЕ КОМАНД ВЫДАЧИ» На основе спецификации был создан тестовый драйвер — класс TCommandTester, наследующий функциональность абстрактного класса Tester. public class Log { static private Streamwriter log=new Streamwriter("log.log"); //Создание лог файла static public void Add(string msg) //Добавление сообщения в лог файл { log.WriteLine(msg); } static public void Close() //Закрыть лог файл { log.Close(); } }' abstract class Tester { protected void LogMessage(string s) //Добавление сообщения в лог-файл
{ Log.Add(s); } } class TCommandTester:Tester // Тестовый драйвер { TCommand OUT; public TCommandTester() { OUT=new TCommand(); Run(); } private void Run() { TCommandTestl() ; } private void TCommandTestl() { int[] commands = {-1, 1, 2, 4, 6, 20}; for(int i=0;i<=5;i++) { OUT.NameCommand=commands[i]; LogMessage(commands[i].ToString()+» : "+OUT.GetFullName()); } } [STAThread] static void Main() { TCommandTester CommandTester = new TCommandTester(); Log.Close(); } } Рис. 2.1. Тестовый драйвер
Класс TCommandTester содержит метод TCommandTestl (), в котором реализована вся функциональность теста. В данном случае для покрытия спецификации достаточно перебрать следующие значения кодов команд: -1, 1, 2, 4, 6, 20, где -1 — запрещенное значение, и получить соответству- ющие им полное название команды с помощью метода GetFullNameO. Пары соответствующих значений заносятся в log-файл для последующей проверки на соответствие спецификации. Таким образом, для тестирования любого метода класса необходимо: • Определить, какая часть функциональности метода должна быть протестирована, то есть при каких условиях он должен вызывать- ся. Под условиями здесь понимаются параметры вызова методов, значения полей и свойств объектов, наличие и содержимое ис- пользуемых файлов и т. д. • Создать тестовое окружение, обеспечивающее требуемые условия. • Запустить тестовое окружение на выполнение. • Обеспечить сохранение результатов в файл для их последующей проверки. • После завершения выполнения сравнить полученные результаты со спецификацией. Описание тестовых процедур Как запустить тест? Для того чтобы запустить тест, нужно: • В методе Run тестового драйвера TCommandTester вызвать метод TCommandTestl, реализующий тест. • Собрать и запустить приложение. Проверка результатов выполнения тестов (сравнение с ожидаемым результатом) После завершения теста следует просмотреть текстовый журнал те- ста (..\ModuleTesting\bin\Debug\log.log), чтобы сравнить полученные ре- зультаты с ожидаемыми результатами, заданными в спецификации тесто- вого случая TCommandTestl. Журнал теста: -1 : ОШИБКА : Неверный код команды 1 : ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ 2 : ОТПРАВИТЬ ИЗ ЯЧЕЙКИ В ВЫХОДНУЮ ЯЧЕЙКУ 4 : ПОЛОЖИТЬ В РЕЗЕРВ 6 : ПРОИЗВЕСТИ ЗАНУЛЕНИЕ 20 : ЗАВЕРШЕНИЕ КОМАНД ВЫДАЧИ
Задание 1 Для остальных примитивных классов (табл. 2.1) в соответствии с приведенным примером необходимо самостоятельно разработать специ- фикации тестовых случаев, соответствующие тесты и провести тестирова- ние. Отчет требуется составить в следующей форме (рис. 2.2): Название тестового случая: Тестировщик: Тест пройден: Да/Нет (PASS/FA1L) Степень важности ошибки: Фатальная (3 уровень — crash) Серьезная (2 уровень — расхождение в спецификации) Незначительная (1 уровень — незначительная ошибка) Описание проблемы: Как воспроизвести ошибку: Предлагаемое исправление (необязательно): Комментарий тестировщика (необязательно): Рис. 2.2. Тестовый отчет
и.. ...I..- ... .. .. ..... . . -:-— Семинар 3. Интеграционное тестирование Основное назначение тестирования взаимодействий состоит в том, чтобы убедиться, что происходит правильный обмен сообщениями меж- ду объектами, классы которых уже прошли тестирование в автономном режиме (на модульном уровне тестирования). Тестирование взаимодействия или интеграционное тестирование представляет собой тестирование собранных вместе, взаимодействую- щих модулей (объектов). В интеграционном тестировании можно объе- динять разное количество объектов — от двух до всех объектов тестируе- мой системы. Интеграционное тестирование отличается от системного тем, что: • при интеграционном тестировании используется подход «белого ящика», а при системном — «черного ящика»; • целью интеграционного тестирования является только проверка правильности взаимодействия объектов, тогда как целью систем- ного — проверка правильности функционирования системы в це- лом. Идентификация взаимодействий Взаимодействие объектов представляет собой просто запрос одного объекта (отправителя) на выполнение другим объектом (получателем) одной из операций получателя и всех видов обработки, необходимых для завершения этого запроса. В ситуациях, когда в качестве основы тестирования взаимодействий объектов выбраны только спецификации общедоступных операций, тес- тирование намного проще, чем когда такой основой служит реализация. Мы ограничимся тестированием общедоступного интерфейса. Такой подход вполне оправдан, поскольку мы полагаем, что классы уже успеш- но прошли модульное тестирование. Тем не менее, выбор такого подхода отнюдь не означает, что не нужно возвращаться к спецификациям клас- сов, дабы убедиться в том, что тот или иной метод выполнил все необхо- димые вычисления. Это обусловливает необходимость проверки значе- ний атрибутов внутреннего состояния получателя, в том числе любых аг- регированных атрибутов, т.е. атрибутов, которые сами являются объекта- ми. Основное внимание уделяется отбору тестов на основе спецификации каждой операции из общедоступного интерфейса класса. Взаимодействия неявно предполагаются в спецификации класса, в которой установлены ссылки на другие объекты. В разделе 4 рассматрива- лось тестирование примитивных классов. Такие объекты представляют собой простейшие компоненты системы и, несомненно, играют важную
роль при выполнении любой программы. Тем не менее, в объектно-ори- ентированной программе существует сравнительно небольшое количест- во примитивных классов, которые реалистично моделируют объекты за- дачи и все отношения между этими объектами. Обычным явлением для хорошо спроектированных объектно-ориентированных программ явля- ется использование непримитивных классов; в этих программах им отво- дится главенствующая роль. Выявить такие взаимодействующие классы можно, используя от- ношения ассоциации (в том числе отношения агрегирования и компо- зиции), представленные на диаграмме классов. Ассоциации такого ро- да преобразуются в интерфейсы класса, а тот или иной класс взаимо- действует с другими классами посредством одного или нескольких спо- собов: Тип 1. Общедоступная операция имеет один или большее число фор- мальных параметров объектного типа. Сообщение устанавливает ас- социацию между получателем и параметром, которая позволяет по- лучателю взаимодействовать с этим параметрическим объектом. Тип 2. Общедоступная операция возвращает значения объектного типа. На класс может быть возложена задача создания возвращаемо- го объекта, либо он может возвращать модифицированный пара- метр. Тип 3. Метод одного класса создает экземпляр другого класса как часть своей реализации. Тип 4. Метод одного класса ссылается на глобальный экземпляр некоторого другого класса. Разумеется, принципы хорошего тона в проектировании рекомендуют минимальное использование гло- бальных объектов. Если реализация какого-либо класса ссылается на некоторый глобальный объект, рассматривайте его как неявный параметр в методах, которые на него ссылаются. Приведем еще раз таблицу разделения классов на примитивные и непримитивные типы (табл. 3.1): Табл. 3.1. Типы классов Класс Тип Класс Тип TBearingParam Примитивный TStore Непримитивный TAxleParam Примитивный TTerminalBearing Непримитивный TCommand Примитивный TTerminalAxle Непримитивный TLog Примитивный TModel Непримитивный TCommandQueue Непримитивный MainForm Непримитивный
Табл. 3.2. Типы взаимодействия классов Непримитивные типы Tbea- ring Param Taxle Param Tcom- TCom- TStore TTer- TTer- minal Axle TLog Tmo- del mand mand Queue minal Bea- ring TCommandQueue 1 1 3 1 1 TStore 3 1 1 TTerminalBear ing 3 TTerminalAxle 3 TModel 3 3 3 3 3 MainForm 3 Таким образом, интеграционному тестированию будут подвергнуты взаимодействия перечисленных непримитивных классов. Выбор тестовых случаев Исчерпывающее тестирование, другими словами, прогон каждого возможного тестового случая, покрывающего каждое сочетание значений — это, вне всяких сомнений, надежный подход к тестированию. Однако во многих ситуациях количество тестовых случаев достигает таких больших значений, что обычными методами с ними справиться попросту невоз- можно. Если имеется принципиальная возможность построения такого большого количества тестовых случаев, на построение и выполнение ко- торых не хватит никакого времени, должен быть разработан систематиче- ский метод определения, какими из тестовых случаев следует воспользо- ваться. Если есть выбор, то мы отдаем предпочтение таким тестовым слу- чаям, которые позволяют найти ошибки, в обнаружении которых мы за- интересованы больше всего. Существуют различные способы определения, какое подмножество из множества всех возможных тестовых случаев следует выбирать. При любом подходе мы заинтересованы в том, чтобы систематически повы- шать уровень покрытия. Подробное описание тестового случая Продемонстрируем тестирование взаимодействий на примере клас- са TCommandQueue. Из табл. 3.2, которая была составлена на основе специ-
фикаций классов, описанных в приложении 2, видно, что класс очереди команд взаимодействует со следующими классами: TBearingParam, TAxleParam, TCommand, TStore, TterminalBearing. С объектом TCommand осуществляется взаимодействие третьего ти- па, т. е. TCommandQueue создает объекты класса TCommand как часть сво- ей внутренней реализации. С остальными классами осуществляется вза- имодействие первого типа: ссылки на объекты классов TStore и TTerminalBearing передаются как параметры в конструктор TCommandQueue, а ссылки на объекты классов TBearingParam и TAxleParam передаются в метод TCommandQueue. AddCommand. Одновременно с изучением этого раздела можно открыть проект IntegrationTesting\IntegrationTests.sln. Для тестирования взаимодействия класса TCommandQueue и класса TCommand, так же, как и при модульном тестировании, разработаем спе- цификацию тестового случая: Табл. 3.3. Спецификация тестового случая Названия взаимодействующих Название теста: классов: TCommandQueue, TCommandQueueTestl TCommand Описание теста: тест проверяет возможность создания объекта типа TCommand и добавления его в очередь при вызове метода AddCommand Начальные условия: очередь команд пуста Ожидаемый результат: в очередь будет добавлена одна команда На основе этой спецификации был разработан тестовый драйвер — класс TCommandQueueTester, который наследуется от класса Tester. Этот класс содержит: • Метод Init, в котором создаются’ объекты классов TStore, TterminalBearing и объект типа TcommandQueue. Этот метод необходимо вызывать в начале каждого теста, чтобы тестируемые объекты создавались вновь:
private void Init() { ТВ = new TTerminalBearing() ; S = new TStore() ; CommandQueue=new TcommandQueue(S,ТВ); S.CommandQueue=CommandQueue; } Рис. 3.1. Метод Init • Методы, реализующие тесты. Каждый тест реализован в отдель- ном методе. • Метод Run, в котором вызываются методы тестов. • Метод dump, который сохраняет в log-файле теста информацию обо всех командах, находящихся в очереди в формате — номер по- зиции в очереди: полное название команды. • Точку входа в программу — метод Main, в котором происходит со- здание экземпляра класса TCommandQueueTester и запуск метода Run. Сначала создадим тест, который проверяет, создается ли объект типа TCommand, и добавляется ли команда в конец очереди. private void TCommandQueueTestl() { Init () ; LogMessage("///////// TcommandQueue Testi /////////////"); LogMessage("Проверяем, создается ли объект типа TCommand"); // В очереди нет команд dump(); // Добавляем команду // параметр = -1 означает, что команда должна быть добавлена //в конец очереди CommandQueue.AddCommand(TCommand.GetR,0,0,0,new TBearingParam(),new TAxleParam(),-l); LogMessage("Command added"); // В очереди одна команда dump () ; } Рис. 3.2. Тест, проверяющий создание объекта типа TCommand
В этот класс включены еще два разработанных теста. Описание тестовых процедур Как запустить тест? Для выполнения этого теста в методе Run необходимо вызвать метод TCommandQueueTestl () и запустить программу на выполнение: private void Run() { TCommandQueueTestl(); } Рис. 3.3. Метод Run Проверка результатов выполнения тестов (сравнение с ожидаемым результатом) После завершения теста следует просмотреть текстовый журнал те- ста (..\lntegrationTesting\bin\Debug\test.log), чтобы сравнить полученные результаты с ожидаемыми результатами, заданными в спецификации тес- тового случая TCommandQueueTestl. //////////////////// TCommandQueue Testi ////////////////// Проверяем, создается ли объект типа TCommand О commands in command queue Command added 1 commands in command queue 0 : ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ Рис. 3.4. Журнал теста Задание 2 Для тестирования взаимодействия остальных непримитивных клас- сов (табл. 2.1) по аналогии с приведенным примером требуется самостоя- тельно разработать спецификации тестовых случаев, соответствующие тесты, провести тестирование и составить тестовые отчеты (рис. 2.2).
Семинар 4. Системное тестирование Системное тестирование качественно отличается от интеграционно- го и модульного уровней. Системное тестирование рассматривает систе- му в целом и применяется на уровне пользовательских интерфейсов, в от- личие от последних фаз интеграционного тестирования, которое опери- рует на уровне интерфейсов модулей, хотя набор модулей может быть аналогичным. Различны и цели этих уровней тестирования. На уровне системы часто сложно и малоэффективно анализировать прохождение тестовых траекторий внутри программы, а также отслеживать правиль- ность работы конкретных функций. Основной задачей системного тести- рования является выявление дефектов, связанных с работой системы в целом, таких как неверное использование ресурсов системы, непредусмо- тренные комбинации данных пользовательского уровня, несовмести- мость с окружением, непредусмотренные сценарии использования, от- сутствующая или неверная функциональность, неудобство в использова- нии и тому подобное. Поскольку системное тестирование проводится на уровне пользова- тельских интерфейсов, то построение специальной тестовой системы ста- новится технически необязательным. Однако объемы данных на этом уровне таковы, что обычно более эффективным подходом является пол- ная или частичная автоматизация тестирования, что может потребовать создания тестовой системы намного более сложной, чем система тестиро- вания на уровне модулей или их комбинаций. Необходимо подчеркнуть, что существует два принципиально раз- ных подхода к системному тестированию. В первом варианте для построения тестов, используются требования к системе, например, для каждого требования строится тест, кото- рый проверяет выполнение данного требования в системе. Этот под- ход особенно широко применяется при разработке военных и науч- ных систем, когда заказчик вполне осознает, какая функциональ- ность ему нужна, и составляет полный набор формальных требова- ний. Тестировщик в данном случае только проверяет, соответствует ли разработанная система этому набору. Такой подход предполагает длинную и дорогостоящую фазу сбора требований, выполняемую до начала собственно проекта. В этом случае для определения требова- ний обычно разрабатывается прототип будущей системы. Во втором подходе основой для построения тестов служит представ- ление о способах использования продукта и о задачах, которые он решает. На основе более или менее формальной модели пользовате- ля создаются случаи использования системы, по которым затем стро- ятся собственно тестовые случаи. Случай использования (use case)
описывает, как субъект использует систему, чтобы выполнить ту или иную задачу Субъекты или актеры (actors) могут исполнять различ- ные роли при работе с системой. Случаи использования могут описы- ваться с различной степенью абстракции. Случаи использования не обязательно охватывают каждое требование. Можно конкретизи- ровать случаи использования и расширять их в наборы более специфи- ческих случаев использования (пошаговое описание случая использова- ния). В контексте конкретного случая использования можно опреде- лить один или большее число сценариев. Сценарий представляет кон- кретный экземпляр случая использования — путь в пошаговом описа- нии случая использования. Каждый путь (сценарий) в случае использо- вания должен быть протестирован (рис. 4.1). Входные данные для каждого сценария надо выбирать следующим образом: • Идентифицировать все значения (входные данные), которые мо- гут задавать субъекты для случая использования. • Определить классы эквивалентности для каждого типа входных данных. • Построить таблицу со списком значений из различных классов эквивалентности. • Построить тестовые случаи на базе таблицы с учетом внешних ог- раничений. Далее при построении тестовых случаев применялись оба подхода и при выполнении заданий необходимо действовать следующим образом: • На основе требований определить случаи использования (use case)
• На основе каждого случая использования (use case) построить сценарии. • Для каждого сценария разработать тестовые случаи (набор тестов). Случаи использования (use cases) Описание случая использования (use case) «подбор подшипников для оси» Последовательно приходят два подшипника, поступает запрос от оси. При поступлении запроса от оси система подбирает два подшипни- ка из имеющихся на складе и выдает их в выходную ячейку. Рассмотрим этот случай использования подробнее. Согласно специ- фикации, система постоянно опрашивает склад и терминал оси. При по- ступлении подшипника (статус склада 32) система опрашивает терминал подшипника, формирует и посылает команду складу «принять подшип- ник» и получает ответ от склада о результатах выполнения команды. При поступлении оси (поступлении параметров оси при опросе терминала оси) система должна подобрать подшипники из имеющихся на складе, сформировать команды для их выдачи, послать их складу и получить от- вет о результате выполнения команд. Далее приводится пошаговое описание этого случая использования: Приняли на склад первый подшипник (1-10) Приняли на склад второй подшипник (11-20) Поступила ось (21-26) Подбираем первый подшипник для оси (27-30) Подбираем второй подшипник для оси (3,1-34) Завершение выдачи команд (35-39). Пошаговое описание случая использования Пошаговое описание приведено на рис. 4.2. Список альтернативных путей В пошаговом описании случая использования рассматривался оптими- стический сценарий, например при анализе статуса склада в пунктах 3 и 11 считалось, что поступил подшипник. Но необходимо рассмотреть и воз- можные альтернативные сценарии: 3,11— анализ статуса склада: З.а, 11.а — подшипник в манипуляторе. З.Ь, 1 l.b — склад свободен. З.с, 11 .с — ошибочное состояние.
Терминал оси Терминал подшипника Склад Система 3. Возрат характеристик оси 2. Возрат статуса 4. Возрат характеристик подшипника 5. Опрос терминала оси 3 При поступлении подшипника запрос характеристик подшипника 1. Опрос статуса склада 12. Возрат статуса 22. Возрат статуса 8. Возрат сообщения о приеме команды 18. Возрат сообшения о приеме команды 16. Возрат характеристик оси 24. Возрат характеристик подшипника 14. Возрат характеристик подшипника 26 Возрат характеристик оси 10. Возрат сообщения о результатах выполнения команды 20. Возрат сообшения о результатах выполнения команды 11. Опрос статуса склада 15. Опрос терминала оси 21. Опрос статуса склада 25. Опрос терминала оси 39. КОНЕЦ 23. При поступлении подшипника запрос характеристик подшипника 28. Возрат сообшения о приеме команды 13. При поступлении подшипника запрос характеристик подшипника 27. Посылка складу команды ♦подобрать подшипник» 31. Посылка складу команды ♦подобрать подшипник» 17. Посылка складу команды ♦положить подшипник» 35. Посылка складу команды «завершение выдачи» 19. Опрос склада о результатах выполнения команды 33. Опрос склада о результатах выполнения команды 29. Опрос склада о результатах выполнения команды 33. Опрос склада о результатах выполнения команды 9 Опрос склада о результатах выполнения команды 7. Посылка складу команды ♦положить подшипник» 30. Возрат сообшения о результатах выполнения команды 32. Возрат сообщения о приеме команды 34. Возрат сообщения о ре зультатах вы пол нения команды 36. Возрат сообшения о приеме команды 38. Возрат сообщения о результатах выполнения команды Рис. 4.2. Пример use case
При получении сообщения от склада о выполнении команды счита- лось, что команда выполнена без ошибки, но здесь тоже существуют аль- тернативные варианты: 8, 16, 22, 24, 26 — получение сообщения о выполнении команды: 8.а, 16.а, 22.а, 24.а, 26.а — нет склада. 8.Ь, 16.Ь, 22.Ь, 24.Ь, 26.Ь — нет сообщения. 8.с, 16.с, 22.с, 24.с, 26.с — команда выполнена с ошибкой. Список альтернативных вариантов для рассмотренного случая ис- пользования можно продолжить. Спецификация тестового случая №1 Состояние окружения (входные данные): Статус склада (StoreStat=32). Пришел подшипник. Статус обмена с терминалом подшипника (0 — есть подшипник) и его параметры (RollerРаг="О NewUser Depotl 123456 1 12 1 1"). Статус обмена с терминалом оси (1 — нет оси) и ее параметры (AxlePar="l NewUser Depotl 123456 1 0 12 12"). Статус команды (CommandStatus=0). Команда успешно принята. Сообщение от склада (StoreMessage=l). Команда успешно выполнена. Ожидаемая последовательность событий (выходные данные): Система запрашивает статус склада (вызов функции GetStoreStat) и получает 32. Система запрашивает параметры подшипника (вызов функции GetRo Iler Par) и получает 0 NewUser Depotl 123456 1 12 1 1. Система Запрашивает параметры оси (вызов функции GetAxlePar) и получает 1 NewUser Depotl 123456 1 0 12 12. Система добавляет в очередь команд склада на последнее место ко- манду SendR (получить из приемника в ячейку) (вызов функции SendStoreCom) и получает сообщение о том, что команда успешно принята — 0. Система запрашивает склад о результатах выполнения команды (вы- зов функции GetStoreMessage) и получает сообщение о том, что ко- манда успешно выполнена — 1. Система запрашивает статус склада (вызов функции GetStoreStat) и получает 32. Система запрашивает параметры подшипника (вызов функции GetRollerPar) и получает 0 NewUser Depotl 123456 1 12 1 1. Система запрашивает параметры оси (вызов функции GetAxlePar) и получает! NewUser Depotl 123456 1 0 12 12.
Система добавляет в очередь команд склада на первое место коман- ду GetR (получить из приемника в ячейку) (вызов функции SendStoreCom) и получает сообщение о том, что команда успешно принята — 0. Система запрашивает склад о результатах выполнения команды (вызов функции GetStoreMessage) и получает сообщение о том, что команда успешно выполнена — 1. Изменяем состояние окружения (входные данные): Статус обмена с терминалом подшипника (1 — нет подшипника) и его параметры (Roll er Par ="1 NewUser Depotl 123456 1 12 1 1"). Статус обмена с терминалом оси (0 — есть ось) и ее параметры (AxlePar="0 NewUser Depotl 123456 1 0 12 12"). Ожидаемая последовательность событий (выходные данные): Система запрашивает статус склада (вызов функции GetStoreStat) и получает 3 2. Система запрашивает параметры подшипника (вызов функции GetRollerPar) и получает 1 NewUser Depotl 123456 1 12 1 1. Система запрашивает параметры оси (вызов функции GetAxlePar) и получаетО NewUser Depotl 123456 1 0 12 12. Система добавляет в очередь команд склада на последнее место ко- манду SendR (вызов функции SendStoreCom) и получает сообщение о том, что команда успешно принята — 0. Система запрашивает склад о результатах выполнения команды (вызов функции GetStoreMessage) и получает сообщение о том, что команда успешно выполнена — 1. Система добавляет в очередь команд склада на последнее место ко- манду SendR (вызов функции SendStoreCom) и получает сообщение о том, что команда успешно принята — 0. Система запрашивает склад о результатах выполнения команды (вызов функции GetStoreMessage) и получает сообщение о том, что команда успешно выполнена — 1. Система добавляет в очередь команд склада на последнее место ко- манду Term (вызов функции SendStoreCom) и получает сообщение о том, что команда успешно принята — 0. Система запрашивает склад о результатах выполнения команды (вызов функции GetStoreMessage) и получает сообщение о том, что команда успешно выполнена — 1. Во всех последующих разделах будет подробно рассматриваться именно этот тестовый случай!
Описание процесса системного тестирования Рассмотрим процесс системного тестирования: Анализ. Тестируемая система анализируется (проверяется) на нали- чие определенных свойств, которым надо уделить особое внимание, и определяются соответствующие шестовые случаи. Построение. Выбранные на стадии анализа тестовые случаи перево- дятся на язык программирования. Выполнение и анализ результатов. Производится выполнение тесто- вых случаев. Полученные результаты анализируются, чтобы опреде- лить, успешно ли прошла система испытания на тестовом наборе. Процесс запуска тестовых случаев и анализа полученных результатов должен быть подробно описан в тестовых процедурах. Далее мы рассмотрим три различных подхода, которые используют- ся при системном тестировании: • Ручное тестирование. • Автоматизация выполнения и проверки результатов тестирования с помощью скриптов. • Автоматическая генерация тестов на основе формального описа- ния.
Семинар 5. Ручное тестирование Наиболее распространенным способом разработки тестов является создание тестового кода вручную. Такой способ создания тестов является наиболее гибким, однако производительность тестировщиков при созда- нии тестового кода соизмерима с производительностью разработчиков при создании кода продукта, а объемы тестового кода часто бывают в 1 -10 раз больше объема самого продукта. В этом случае запуск тестов осуществляется вручную. Проверку, про- шла ли тестируемая система испытания на заданном тестовом случае, те- стировщик также осуществляет вручную, сравнивая фактические резуль- таты журнала теста с ожидаемыми результатами, описанными в специ- фикации тестового случая. Функции dll-библиотеки обеспечивают обращение к серверу для по- лучения информации о состоянии элементов комплекса и возвращают серверу информацию о функционировании системы. Значит, для модели- рования состояния окружения (входных данных) необходимо создать спе- циальный сервер. Кроме того, необходимо сохранять получаемую от сервера информа- цию о функционировании системы (выходные данные) в журнале (рис. 5.1). Рис. 5.1. Система и ее окружение (ручное тестирование)
При разработке тестов был использован следующий подход: • состояние окружения задается в тесте {входные данные); • в тесте создается сервер: сервер по запросу от dll передает информацию о заданном состо- янии окружения; сервер получает от dll информацию о функционировании системы; • получаемая информация сохраняется в журнале теста. Подробное описание тестового случая № 1 Ознакомление с настоящим пунктом полезно предварить изучением п. 7, содержащего описание ручного тестирования. Здесь рассматривает- ся та часть теста на С#, которую вам придется написать самостоятельно при выполнении заданий. Приведенный пример был разработан в соот- ветствии со спецификацией тестового случая N1. Для простоты будем считать, что события происходят последовательно в строго заданном по- рядке. Реально наша система представляет собой многопоточное прило- жение, поэтому мы не можем это гарантировать. class Testi:Test { override public void start() {//Задаем состояние окружения (входные данные) StoreStat="32"; //Поступил подшипник RollerPar="0 NewUser Depotl 123456 1 12 1 1"; //статус обмена с терминалом подшипника (0 - есть подшипник) //и его параметры AxlePar="l NewUser Depotl 123456 1 0 12 12"; //статус обмена с терминалом оси (1 — нет оси) и ее параметры CommandStatus="0"; //команда успешно принята StoreMessage="l"; //успешно выполнена //Получаем информацию о функционировании системы wait("GetStoreStat"); //опрос статуса склада wait("GetRollerPar"); //Получение информации о подшипнике с терминала подшипника wait("GetAxlePar"); //Получение информации об оси с терминала оси wait("SendStoreCom"); //добавление в очередь команд склада на первое место //команды GetR (получить из приемника в ячейку) wait("GetStoreMessage"); //Получение сообщения от склада о результатах выполнения команды //В результате первый подшипник должен быть принят wait("GetStoreStat"); //опрос статуса склада wait("GetRollerPar") ;
//Получение информации о подшипнике с терминала подшипника wait("GetAxlePar "); //Получение информации об оси с терминала оси wait("SendStoreCom"); //добавление в очередь команд склада на первое место //команды GetR (получить из приемника в ячейку) wait("GetStoreMessage"); //Получение сообщения от склада о результатах выполнения //команды. В результате второй подшипник должен быть принят //Задаем новое состояние окружения (входные данные) RollerPar="l NewUser Depotl 123456 1 12 1 1"; //статус обмена с терминалом подшипника (1 - нет подшипника) //и его параметры AxlePar="0 NewUser Depotl 123456 1 0 12 12"; //статус обмена с терминалом оси (0 - есть ось) и ее параметры //Получаем информацию о функционировании системы wait("GetStoreStat"); //опрос статуса склада wait("GetRollerPar"); //Получение информации о подшипнике с терминала подшипника wait("GetAxlePar"); //Получение информации об оси с терминала оси wait("SendStoreCom");//Добавление в очередь команд склада на последнее место //команды SendR (ячейку на выход) wait("GetStoreMessage"); //Получение сообщения от склада о результатах выполнения //команды //В результате первый подшипник для оси должен быть выдан wait("SendStoreCom"); //Добавление в очередь команд склада на последнее место //команды SendR (ячейку на выход) wait("GetStoreMessage"); //Получение сообщения от склада о результатах выполнения //команды. //В результате второй подшипник для оси должен быть выдан wait("SendStoreCom"); //Добавление в очередь команд склада на последнее место //команды Term (завершение команд выдачи) wait("GetStoreMessage"); //Получение сообщения от склада о результатах выполнения //команды finish(); } } Рис. 5.2. Пример фрагмента теста (вариант 1)
При разработке тестов не обязательно дожидаться каждого события, которое должно происходить в соответствии со случаем использования. До- статочно вызвать wait для событий, после наступления которых надо менять состояние окружения. В период ожидания наступления события, заданного в wait, может происходить любое количество других событий. Все эти собы- тия будут занесены в журнал. При необходимости ждать не первого, а n-го вызова можно вызывать wait с одним и тем же параметром п раз (например, в цикле). При таком подходе тест будет гораздо короче, на- пример приведенный выше тест будет выглядеть следующим образом: class Testi:Test override public void start() { StoreStat="32";//Пришел подшипник RollerPar="0 NewUser Depotl 123456 1 12 1 1";//его параметры AxlePar="l NewUser Depotl 123456 1 0 12 12";//нет оси CommandStatus="0";//команда успешно принята StoreMessage="l";//команда успешно выполнена wait("SendStoreCom")///первый подшипник принят wait("SendStoreCom");//второй подшипник принят RollerPar="l NewUser Depotl 123456 1 12 1 1"; //больше нет подшипников AxlePar="0 NewUser Depotl 123456 1 0 12 12";//есть ось wait("SendStoreCom");I/выдача подшипника wait("SendStoreCom");//выдача подшипника wait("SendStoreCom");//завершение выдачи finish();} } Рис. 5.3. Пример фрагмента теста (вариант 2) Описание тестовых процедур Тестовые процедуры — это формальный документ, содержащий опи- сание необходимых шагов для выполнения тестового набора. В случае ручных тестов тестовые процедуры содержат полное описание всех шагов и проверок, позволяющих протестировать продукт и вынести вердикт PASS/FAIL. Процедуры должны быть составлены таким образом, чтобы любой инженер, не связанный с данным проектом, был способен адек- ватно провести цикл тестирования, обладая только самыми базовыми знаниями о применяющемся инструментарии.
Как запустить тест Для того чтобы запустить тест, нужно: В методе Classi. Main создать экземпляр нового класса и вызвать его метод start. Так как метод start является виртуальным, то можно об- ращаться к тестам, используя ссылку на тип Test. Это делается так: Test t = new <ваш класс-потомок Test>; t.start(); Собрать и запустить приложение. Проверка результатов выполнения тестов (сравнение с ожидаемым результатом) После завершения теста следует просмотреть текстовый журнал те- ста (..\SystemTesting\ManualTests\Tests\bin\Debug\log.log), чтобы выяс- нить, какая последовательность событий в системе была реально зафик- сирована (выходные данные) и сравнить их с ожидаемыми результатами, заданными в спецификации тестового случая №1. Как уже упоминалось, кроме журнала теста, создается еще и соот- ветствующий журнал системы. В обоих журналах содержится информа- ция о происходивших в системе событиях. До того, как вручную сравни- вать журнал теста с ожидаемыми результатами, заданными в специфика- ции тестового случая, вы можете использовать SystemLogAnimator (п. 14) для визуализации соответствующего журнала системы и получить на- глядное (в том числе ретроспективное) представление о том, как функ- ционировала система. Это важно при разработке тестовых случаев, пото- му что, не зная в деталях, как работает система, можно написать непра- вильный тест. Необходимо научиться отличать, что вы в действительно- сти обнаружили — ошибку в системе или результат работы неправильно- го теста. Пример неправильного теста Предположим, что вы не поняли Приложение 2 (FS) во всех деталях и неправильно составили спецификацию тестового случая №1 и тест. Например: //Задаем состояние окружения (входные данные) StoreStat="32"; //Поступил подшипник • • • //Получаем информацию о функционировании системы wait ("GetStoreStat"); //опрос статуса склада //Вместо того, чтобы получить информацию о подшипнике с //терминала подшипника, мы хотим получить информацию //об оси с терминала оси
wait("GetAxlePar"); В журнале теста мы увидим следующую информацию: CALL: GetStoreStat О RETURN: 32 CALL: GetRollerPar RETURN: 0 NewUser Depotl 123456 1 12 1 1 CALL: GetAxlePar RETURN: 1 NewUser Depotl 123456 1 0 12 12 Главное — суметь разобраться и исправить спецификацию тестового случая и тест, если вы сами допустили ошибку. Задание 3 Для тестового случая № 1 необходимо составить полный список всех возможных альтернативных путей (см. подраздел «Список альтернатив- ных путей») и разработать соответствующие тесты. Кроме того, необходимо: • выбрать случай использования на основании дерева решений (. .\SystemTesting\Decision Tree.vsd); • составить пошаговое описание выбранного случая использования; • учесть все альтернативные пути; • составить спецификации тестовых случаев; • разработать соответствующие тестовые случаи (тесты)', • составить тестовые отчеты (рис. 2.2).
Семинар 6. Автоматизация тестирования с помощью скриптов Общая тенденция последнего времени предусматривает максималь- ную автоматизацию тестирования, которая позволяет справляться с большими объемами данных и тестов, необходимых для современных продуктов. В этом случае запуск тестов и проверка того, что тестируемая систе- ма прошла испытания на заданном тестовом случае, будет осуществлять- ся автоматически. Здесь еще раз повторим, что функции в dll были переписаны так, что они обращаются к серверу для получения информации о состоянии элементов комплекса и возвращают серверу информацию о функциони- ровании системы. Для задания состояния окружения (входных данных) необходимо обратиться к серверу и передавать ему необходимую ин- формацию. В данном случае был разработан сервер, который кроме приема и передачи информации еще осуществляет проверку правильно- сти поведения системы (рис. 6.1). Он представляет собой модель тести- руемой системы. В рамках модели заданы ожидаемые результаты и осу- ществляется сравнение выходных данных с ожидаемыми результатами. Хотя считалось, что разработанная модель является корректной, полной и непротиворечивой, у вас есть реальная возможность найти ошибки и в самой модели. При разработке тестов был использован следующий подход: • состояние окружения задается в тесте (входные данные)', • разработанный сервер: передает информацию о заданном состоянии окружении по за- просу от dll; получает от dll информацию о функционировании системы (выходные данные)', сравнивает выходные данные с ожидаемым результатом. • получаемая информация сохраняется в журнале теста; • строится таблица покрытия FS (..\SystemTesting\ScriptTests\Logs\summary.html). Подробное описание тестового случая № 1 Изучение настоящего пункта полезно предварить ознакомлением с п.8, в котором описан подход к автоматизации тестирования с помощью языка скриптов. Здесь рассматривается тест на tel, подобные тесты необхо- димо будет писать самостоятельно при выполнении заданий. Приведенный пример был разработан в соответствии со спецификацией тестового случая
№1. Для простоты будем считать, что события происходят последовательно в строго заданном порядке. Реально наша система — многопоточное прило- жение, поэтому такое условие далеко не всегда можно гарантировать. Рис. 6.1. Система и ее окружение (скрипты) source binWsrv.tcl //запуск сервера global StoreStat //статус склада global RollerPar //терминал подшипника global AxlePar //терминал оси global CommandStatus //возвращаемое значение функции //SendStoreCom о результатах получения команды global StoreMessage //сообщение от склада о результатах выполнения команды global rollers_found //1 (можно подобрать подходящий //подшипник или 0 (нельзя) global fds //строка для графы "Покрытие FS" в итоговой //таблице результатов тестирования (по умолчанию - Default)
global last_command //последняя команда тестируемой системы global allowed //список разрешенных команд set fds "l.a.l; l.a.4; 2.a; 2.C; З.а; 4.с" //покрывает заданные пункты FS StartTest WarehousetestOOOl //запуск теста Timeout 30 // процесс тестирования будет прерван через 30 сек //Задаем состояние окружения (входные данные) set StoreStat 32 //Поступил подшипник set RollerPar "0 NewUser Depotl 123456 1 12 1 1" //статус обмена с терминалом подшипника //(0 — есть подшипник) и его параметры set AxlePar "1 NewUser Depotl 123456 1 0 12 12" //статус обмена с терминалом оси (1 - нет оси) //и ее параметры set Commandstatus 0 //команда успешно принята set StoreMessage 1 //команда успешно выполнена set rollers_found 1 //можно подобрать подходящий подшипник //Получаем информацию о функционировании системы Wait "GetStoreStat *"01 //Неограниченное время (0) ждем получения команды ("опрос //статуса склада") и если команда не получена, то будет //зафиксирована ошибка (1) Wait "GetRollerPar" 0 1 //Неограниченное время (0) ждем получения команды //("получить информацию о подшипнике с терминала подшипника") //и если команда не получена, то будет зафиксирована ошибка (1) set allowed [list "GetAxlePar"] //Получение команды "получить информацию об оси с терминала //оси" разрешено и не должно вызвать ошибку Wait "SendStoreCom 1 *" 10 1 //В течение 10 секунд ждем получения команды ("добавить в //очередь команд склада на первое место команду GetR (1 — //получить из приемника в ячейку)" и если команда за это //время не получена, то будет зафиксирована ошибка (1) Wait GetStoreMessage 0 1 //Неограниченное время (0) ждем получения команды //("получить сообщение от склада о результатах выполнения //команды") и если команда не получена, то будет //зафиксирована ошибка (1)
//В результате первый подшипник должен быть принят set allowed [list] Wait "GetStoreStat *"01 //Неограниченное время (0) ждем получения команды ("опрос //статуса склада") и если команда не получена, то будет //зафиксирована ошибка (1) Wait GetRollerPar 0 1 //Неограниченное время (0) ждем получения команды //("получить информацию о подшипнике с терминала //подшипника") и если команда не получена, то будет //зафиксирована ошибка (1) set allowed [list "GetAxlePar"] //Получение команды "получить информацию об оси с терминала //оси" разрешено и не должно вызвать ошибку Wait "SendStoreCom 1 *" 10 1 //В течение 10 секунд ждем получения команды ("добавить в //очередь команд склада на первое место команду GetR //(1 -- получить из приемника в ячейку)") и если команда за //это время не получена, то будет зафиксирована ошибка (1) Wait "GetStoreMessage" 0 1 //Неограниченное время (0) ждем получения команды //("получить сообщение от склада о результатах выполнения //команды") и если команда не получена, то будет //зафиксирована ошибка (1) //В результате второй подшипник должен быть принят //Задаем новое состояние окружения (вводные данные) set StoreStat 32 //Поступил подшипник set RollerPar {1NANA 0 0 0 0 0} //статус обмена с терминалом подшипника (1 - нет подшипник) //и его параметры set AxlePar "0 NewUser Depotl 123456 1 0 12 12" //статус обмена с терминалом оси (0 - есть ось) и //ее параметры //Получаем информацию о функционировании системы Wait "GetStoreStat *"01 //Неограниченное время (0) ждем получения команды ("опрос //статуса склада") и если команда не получена, то будет //зафиксирована ошибка (1) Wait "GetRollerPar" 0 1 //Неограниченное время (0) ждем получения команды
//("полунить информацию о подшипнике с терминала //подшипника") и если команда не получена, то будет //зафиксирована ошибка (1) Wait GetAxlePar 0 1 //Неограниченное время (0) ждем получения команды //("получить информацию о оси с терминала оси") и если //команда не получена, //то будет зафиксирована ошибка (1) //В результате должна прийти ось Wait "SendStoreCom 2 *" 10 1 //В течение 10 секунд ждем получения команды ("добавить в //очередь команд склада на последнее место команду SendR //(2 — ячейку на выход)") и если команда за это время не //получена, то будет зафиксирована ошибка (1) if {! [string compare $last_conwand "SendStoreCom 2999001"]} { //была послана команда выдать первый подшипник для оси Wait "GetStoreMessage" 0 1 //Неограниченное время (0) ждем получения команды //("получить сообщение от склада о результатах выполнения //команды") и если команда не получена, то будет //зафиксирована ошибка (1) Wait "SendStoreCom 2 9 9 9 0 1 1" 0 1 //Неограниченное время (0) ждем получения команды //("добавить в очередь команд склада на последнее место //команду SendR (2 - ячейку на выход)") и если команда за //это время не получена, то будет зафиксирована ошибка (1) //послали команду выдать второй подшипник } if {([string compare $last_coirmand "SendStoreCom 2999011"]} { //если была послана команда выдать второй подшипник для оси Wait "GetStoreMessage" 0 1 //Неограниченное время (0) ждем получения команды //("получить сообщение от склада о результатах выполнения //команды") и если команда не получена, то будет //зафиксирована ошибка (1) Wait "SendStoreCom 2999001"01 //Неограниченное время (0) ждем получения команды //("добавить в очередь команд склада на последнее место //команду SendR (2 - ячейку на выход)") и если команда за //это время не получена, то будет зафиксирована ошибка (1) //послали команду выдать первый подшипник
1------------------------------------------------------------ Wait "GetStoreMessage" 0 1 //Неограниченное время (0) ждем получения команды ("получить //сообщение от склада о результатах выполнения команды") и //если команда не получена, то будет зафиксирована ошибка (1) Wait "SendStoreCom 20 *" 0 1 //Неограниченное время (0) ждем получения команды //("добавить в //очередь команд склада на последнее место //команду Term (20 - //завершение команд выдачи)") и если //команда не получена, то будет зафиксирована ошибка (1) Wait "GetStoreMessage" 0 1 //Неограниченное время (0) ждем получения команды //("получить сообщение от склада о результатах выполнения //команды") и если команда не получена, то будет //зафиксирована ошибка (1) EndTest Рис. 6.2. Тест на tcl/tk Описание тестовых процедур Как запустить тест Запустить run.bat. В файле run.bat запускается tests.bat. Файл tests.bat содержит команды запуска тестов и установки необходимого состояния базы данных: osql -Н <Host > -S <Server > -d WhrehouseTCL -U sa -P sa -i sql\ClearDB.sql //Вызывается скрипт ClearDB.sql из подкаталога sql. Предполагает- ся, //что SQL Server выполняется на машине <Host> и называется //<Server>. Эти параметры были настроены автоматически при //ус- тановке практикума. //База данных называется WarehouseTCL. Если названия отличаются, //необходимо заменить параметры командной строки утилиты OSQL: // -h <host> — задает имя хоста, на котором выполняется SQL Server; // -s <server> — имя сервера; // -d <db> — имя базы данных; // -u <usemame> — имя пользователя; // -Р <password> — пароль; И -i <script> — имя скрипта SQL. bin\launcher tests\warehousetest0001 .tel //Вызывается тест warehousetestOOOLtd copy log.txt logs\warehousetest0001 .txt
//Файл log.txt (лог тестируемой системы) копируется в файл //ware- housetestOOOl.txt. Для исключения части тестов из набора //достаточ- но закомментировать соответствующие строки (команда //REM). Проверка результатов выполнения тестов (сравнение с ожидаемым результатом) В этом случае запуск тестов и проверка того, что тестируемая систе- ма прошла испытания на заданном тестовом случае, осуществляется ав- томатически и результаты представляются в виде таблицы; здесь, как и в предыдущем случае создается журнал теста, а также можно использовать SystemLogAnimator для визуализации журнала системы. Пример неправильного теста Рассмотрим тот же пример неправильного теста, как и в случае руч- ного тестирования: При поступлении подшипника вместо того, чтобы получить инфор- мацию о подшипнике с терминала подшипника, мы хотим получить ин- формацию об оси с терминала оси. Убедитесь, что система функционирует по-другому. Задание 4 Нужно выполнить те же задания, что и для ручного тестирования.
Семинар 7. Автоматическая генерация тестов на основе формального описания Тесты составляются на основе спецификации требований. При фор- мулировании требований на естественном языке существует проблема их различных толкований. Одним из способов избежать этого является при- менение формальных языков для описания структуры и поведения систе- мы (UML, SDL, MSC). Кроме того, описание требований на формальном языке является формальным описанием тестовых случаев, на основе ко- торого можно генерировать тестовый код. В практикуме для создания те- стов будет использоваться язык диаграмм взаимодействия (Message Sequence Charts, MSC — п.11). В этом случае под тестом мы будем пони- мать его представление в виде MSC-диаграммы. В Практикуме для реализации тестирования используется учебная система автоматизации тестирования ТАТ — Test Automation Training. На вход система принимает формальное описание тестов в виде MSC диа- грамм (в текстовом формате MSC PR). На основе этих MSC диаграмм и конфигурационного файла (в формате XML), который описывает интер- фейс тестируемой системы, генерируется тест на С#. (Интерфейс тести- руемого приложения (Application Under Test — AUT) содержит сигналы, сообщения, транзакции, которые система может посылать тестовому ок- ружению или может принимать от тестового окружения). Для запуска си- стемы с этим тестом необходимо написать Wrapper, который транслирует сигналы от теста к системе и наоборот. Таким образом, методика тестирования системы с помощью ТАТ выглядит следующим образом: • написать Wrapper для тестируемой системы; • создать файл конфигурации; • создать формальное описание тестов в виде MSC-диаграмм; • нарисовать MSC-диаграммы в MS Visio; • сгенерировать с помощью макроса тестовый файл в формате MPR; • настроить в ConfigTAT проект теста (указать пути) или набора тестов; • запустить тест или набор тестов; • проанализировать получаемые log-файлы. В данном случае wrapper и файл конфигурации (первые два пункта методики) уже созданы, поэтому вам необходимо будет выполнить толь- ко п. 3-6. В рассматриваемом подходе не только запуск тестов и проверка ре- зультатов прогона тестового случая будут осуществляться автоматически, но и сам тестовый код будет генерироваться автоматически на основе MSC-диаграммы (рис. 7.1).
При разработке тестов был использован следующий подход (рис. 6.9): Когда реализуется определенное событие, модель посылает сигнал запроса состояния к тестовому окружению. Рис. 7.1. Система и ее окружение (автоматическая генерация) Состояние окружения задается в тесте (входные данные) в виде пара- метров сигналов. Тест возвращает состояние окружения (StoreStat, AxlePar, RollerPar, StoreMessage, CoinmandStatus), посылая моде- ли сигнал с параметрами в соответствии с запросом. Получаемая информация сохраняется в журнале теста. Рис. 7.2. Взаимодействие теста и модели (MSC-диаграмма)
Подробное описание тестового случая № 1 Изучение материала настоящего пункта полезно предварить ознако- млением с п. 9, содержащим описание подхода к автоматической генера- ции тестов на основе MSC. Здесь рассматривается тест, представляющий собой MSC-диаграмму, созданную в Visio. Подобные тесты необходимо будет разработать (нарисовать) самостоятельно при выполнении заданий. Приведенный пример был разработан в соответствии со спецификацией тестового случая №1 (рис. 7.3). Описание тестовых процедур Как сгенерировать и запустить тест Изучение материала настоящего пункта полезно предварить ознако- млением с п. 12, 13, содержащими описание использования MS Visio для генерации MPR файлов и описание конфигурирования — ConfigTAT. На данном этапе используется тест, представляющий собой MSC- диаграмму, созданную в Visio. Дальнейшие действия описываются следу- ющей методикой: 1) Запустить Microsoft Visio. 2) Загрузить Stensil (File->Open->MSC.VSS или File->Open Stensil ->MSC.VSS). Visio выдаст предупреждение о том, что данный stensil содержит макросы. На предупреждение следует ответить «Enable macros». 3) Открыть существующий тестовый случай №1 — WarehousetestOOOl (. .\SystemTesting\TATTests\Tests\Tests\T ests. vsd). 4) Для генерации MPR вызвать следующий макрос: Tools->Macros ->MSC->Modulel->Parse. В указанной папке будет создан MPR-файл с именем, соответствующим имени текущей страни- цы в Visio (..\SystemTesting\TATTests\Tests\V^rehouseTestl\warehou.setest0001.mpr). 5) Запустить ConfigTAT. 6) В меню File -> Open выбрать тестовый случай №1. 7) Выбрать настройки — установить по умолчанию (Set ALL to default). 8) Запуск — генерация и запуск теста (Run ALL). Проверка результатов выполнения тестов (сравнение с ожидаемым результатом) В этом случае запуск тестов и проверка того, что тестируемая систе- ма прошла испытания на заданном тестовом случае, осуществляется ав- томатически, как и в предыдущем случае создается журнал теста, а также
Рис. 7.3. Тестовый случай № 1
можно использовать SystemLogAnimator (п. 14) для визуализации журнала системы. Для просмотра протоколов тестирования надо использовать группу «Test Logs» ConfigTAT и можно просматривать: протокол тестирования в виде html-страницы (HTML log), протокол тестирования в виде txt файла (Text-log). протоколы в формате mpr (отдельный протокол для каждого testcase- а и каждой итерации теста), которые можно открыть в программе Telelogic нажатием кнопки «View» (MPR logs). Пример теста с ошибкой На рис. 7.4 представлена диаграмма теста с ошибкой. Используя FS, необходимо объяснить причину некорректности тестового случая. Рис. 7.4. Тест с ошибкой Задание 5 Для случая автоматического тестирования на основе MSC-диаграмм нужно повторить те же задания, что и для ручного тестирования.
Семинар 8. Описание ручного тестирования Реализация выбранного подхода для ручного тестирования приво- дится в Classl.cs (..\SystemTesting\ManualTests\Tests\Classl.cs). Все классы входят в пространство имен Tests. Это пространство имен содержит следующие классы: Classi — главный класс приложения. Содержит статический метод Main, вызываемый при запуске; Test — абстрактный (abstract) класс, реализующий общую для всех тестов функциональность. Содержит следующие методы: public Test () — конструктор. Создает серверный сокет и запус- кает сервер; protected void wait (string st) — ожидает получения от dll вызова, начинающегося со строки st; protected void finish()—обрабатывает последний запрос от dll и закрывает серверный сокет; virtual public void start () — запускает тест. В каждом кон- кретном тесте переопределяется. Кроме того, класс Test содержит пять protected полей типа string: StoreStat — статус склада; AxlePar — терминал оси; Roller Par — терминал подшипника; CommandStatus — возвращаемое значение функции SendStoreCom; StoreMessage — сообщение от склада. Как создать свой тест? Для создания нового теста необходимо выполнить следующие дей- ствия: • создать новый класс, являющийся потомком Test; • переопределить в нем метод start (), чтобы реализовать функ циональность теста: — задать состояние окружения (StoreStat, AxlePar, RollerPar, StoreMessage, CominandStatus); — ждать, когда произойдет определенное событие (вызов wait, в котором надо задать строку для выхода из состояния ожида- ния); — задать новое состояние окружения и т.д. • тест должен завершаться вызовом finish ().
В общем виде тест выглядит так: override public void start О { <задание переменных> wait(<строка>); <задание переменных> wait(<строка>); finish(); }
Семинар 9. Автоматизация тестирования с помощью скриптов Как создать свой тест? В данном случае используется тот же принцип, что и при ручном те- стировании: • задать состояние окружения (StoreStat, AxlePar, RollerPar, StoreMessage, CommandStatus); • ждать, когда в системе произойдет определенное событие; • задать новое состояние окружения; • ит.д. Каждый тест должен быть представлен в следующем виде: ЗАГОЛОВОК БЛОК Wait <условие> БЛОК Wait <условие> EndTest Опишем эти элементы более подробно. Описание заголовка Заголовок теста состоит из: • команды запуска сервера; • команд global, устанавливающих доступ к глобальным перемен- ным состояния окружения; • команды StartTest, задающей имя теста (оно не обязательно должно совпадать с именем файла). source bin\\srv.tel //запуск сервера global StoreStat //статус склада global AxlePar //терминал оси global RollerPar //терминал подшипника global Commandstatus //возвращаемое значение функции //SendStoreCom global StoreMessage //сообщение от склада global rollers_found //1 (можно подобрать подходящий //под- шипник) или 0 (нельзя)
global fds //строка для графы «Покрытие FDS» в итоговой //таблице результатов тестирование //(по умолчанию — Default) global last_command //последняя команда тестируемой системы global allowed //список разрешенных команд, их получение //должно вызывать ошибку StartTest <имя> //запуск теста Описание блока Каждый блок устанавливает значения одной или более переменных окружения. Например: Set StoreStat 32 Описание Wait Команда Wait используется для изменения состояния тестового ок- ружения в ответ на действия системы. При вызове процедуры Wait скрипт входит в состояние ожидания. Обращение со стороны системы приводит к завершению состояния ожи- дания. Если условие выполнено, то ответ на это обращение будет сфор- мирован на основе следующего блока, иначе — на основе предыдущего. Wait <команда> <время> <обязательно> <команда> определяет команду системы, которая приведет к перехо- ду в следующий блок. Можно использовать стандартные символы подстановки *, ?, []; <время> определяет время ожидания. Если задать 0, то время не ог- раничено; <обязательно> дает возможность не формировать ошибку, если за заданное время требуемая команда не была получена. Если задана 1, то в случае неполучения команды будет сформирована ошибка, а ес- ли 0, то ошибки не будет. Примеры использования: Wait ZZZZZZZ 10 0 — ждать 10 секунд (команда ZZZZZZZ является недопустимой и не может быть получена) Wait "GetStoreStat ★" 0 1 — ждать команды GetStoreStat с лю- быми параметрами неограниченное время Wait "SendStoreCom 2 *" 10 1 — ждать команды SendStoreCom с первым параметром 2 и произвольными остальными параметрами в течение 10 секунд, если она не получена, будет зафиксирована ошибка Wait ZZZZZZZ 0 0 — бесконечное ожидание.
Так как ожидание в процедуре Wait прерывается только при поступ- лении запроса от системы, то в случае отсутствия запросов (система за- висла и ничего не делает) тест также останавливается. Для предотвраще- ния такой ситуации используется следующая команда: Timeout <время>. <время> определяет число секунд, после которого процесс тестиро- вания будет прерван. При этом в журнал теста будет занесено сообщение TIMEOUT. Срабатывание таймаута приводит к завершению теста. Команда Timeout 0 отменяет ограничение времени. Описание allowed Рассмотрим переменную allowed более подробно. Она содержит список тех команд, получение которых разрешено и не должно вызывать ошибку. Например, в следующем примере вызов GetAxlePar не спрово- цирует ошибку: Set StoreStat 32 Wait "GetStoreStat *"01 Wait "GetRollerPar" 0 1 Set allowed [list "GetAxlePar"] Wait "SendStoreCom 1001000"01 Однако ошибка все же возникнет, если данная команда в данной си- туации является недопустимой. Несмотря на то, что проверка реализова- на в скрипте srv.tcl, использование команды Wait и списка allowed могут ужесточить проверку. Значение по умолчанию для allowed — "*". В этом случае проверку проходят все команды. Вход в процедуру Wait вызывает расширение списка allowed до момента выхода из нее, так что ожидаемая команда системы всегда проходит проверку на принадлежность списку allowed. В списке allowed можно задействовать символы подстановки.
Семинар 10. Описание автоматической генерации MSC тестов Как создать свой тест? В данном случае под тестом мы будем понимать его представление в виде MSC-диаграммы. В качестве объектов мы будем рассматривать тест (Test) и тестируемую систему (Model). Обмен сообщениями между тестом и моделью показан на рис. 10.1. Когда в системе происходит определенное событие, она запрашива- ет состояние окружения, посылая сигнал тестовому окружению. Тест возвращает состояние окружения (StoreStat, AxlePar, RollerPar, StoreMessage, CominandStatus), посылая модели сигнал с па- раметрами в соответствии с запросом. Рис. 10.1. Взаимодействие теста и модели Структура и описание содержимого каталогов В папке Documents находится: FDS; HLD; Практикум (этот документ). Папка IntegrationTesting содержит проект ; Pl Documents IB-C1 IntegrationTesting йП ModuleTesting S Pl SystemTesting Visual Studio .NET с примером интеграционного теста. Папка ModuleTesting содержит проект Visual Studio .NET с примером модульного теста. В папке SystemTesting\ManualTests содержится проект Visual Studio .NET с примерами системных ручных тестов.
В папке SystemTesting\ScriptsTests содер- жатся примеры тестов с использованием скриптов: \bin — содержит программу launcher.exe, файл srv.tcl и вспомогательные скрипты header.tcl и footer.tcl, используемые для формирования структуры html-отчета. Программа launcher.exe запускает тестиру- емую систему и тестовый скрипт на вы- полнение, а после завершения теста завер- шает выполнение системы. \logs — log-файлы пройденных тестов (*./og), log-файлы тестируемой системы (*.txt) и общий отчет summary.html. \model — исполнимые файлы тестируемой системы. \sql — SQL скрипты для установки задан- ного состояния базы данных. \tests — тестовые скрипты на языке TCL. Папка SystemTesting\TATTests содержит: В-О System!esting В О ManualTests Й-Q Tests Й-Q ScriptTests Q Bin {________j Logs Q Model ! -Q SQL Q Tests В Q SysLogAnimator El Q Data В О TATTests Q ConfigTAT В Q tat О bin Q tempi H-Q Tests \ConfigTAT — программа ConfigTAT для настройки и запуска тестов ТАТ: \tat — система автоматизации тестирования ТАТ. \Tests\config\config.xml — xml файл, описывающий тестируемую сис- тему (AUT), тестовое окружение и сигналы между ними. \Tests\Model — тестируемая система application under test. \Tests\mpr — mpr-файлы, описывающие тесты. \Tests\SQLScripts — sql-скрипты для подготовки базы данных к кон- кретному тесту. \Tests\Tests — stencil для Visio и MSC-диаграммы тестов в Visio. \Tests\WareHouseTest№ — рабочие папки тестов. \Tests\ *.tcf, tests.ltc — сохраненные конфигурации тестов и Testsuite- а для ConfigTAT. Папка SysLogAnimator содержит основные файлы: библиотека DirectX, запускаемый файл LogAnimator.exe и конфигурационный файл LogAnimator.cfg. Папка SysLogAnimator\Data содержит необходимые для работы ани- матора графические файлы. Описание MSC Язык диаграмм взаимодействия (Message Sequence Charts, MSC) — это язык описания поведения системы в виде последовательности событий.
События могут относиться к отдельным компонентам системы, к взаимо- действиям между компонентами системы либо к взаимодействию между системой и ее окружением. Основное назначение диаграмм взаимодейст- вия — описание последовательностей допустимых взаимодействий между компонентами системы и системой и ее окружением. Диаграммы изобра- жаются в графическом виде, но существует также текстовая форма MSC- диаграмм. Обе формы переводятся взаимно однозначно друг в друга. Разновидности диаграмм взаимодействия используются при разра- ботке систем реального времени с 1960-х годов. Особое распространение диаграммы взаимодействия получили в области разработки телекомму- никационных систем. Язык диаграмм взаимодействия стандартизован в 1992 году Международным Телекоммуникационным Союзом (ITU-T) (Рекомендация Z.120 1992). В настоящее время принята новая, значи- тельно расширенная версия стандарта (Рекомендация Z.120 1996.). Основные понятия Диаграмма взаимодействия описывает последовательности собы- тий, происходящих с набором объектов (системой взаимосвязанных ком- понентов). Дополнительно каждая система рассматривается как откры- тая, т.е. подразумевается наличие некоторого окружения системы, с кото- рым система взаимодействует. Окружение также может задаваться в виде отдельного объекта. Основным понятием диаграммы взаимодействий является трасса объ- екта. Для каждого объекта на диаграмме имеется отдельная вертикальная ось. На этой оси откладываются события, имеющие отношение к данному объекту. Считается, что все объекты существуют одновременно, и последо- вательности событий объектов развиваются параллельно. При описании объекта используются стартовый (прозрачный прямоугольник) и конеч- ный (черный прямоугольник) символы объекта, обозначающие соответст- венно начало и конец описания объекта в данной MSC-диаграмме. Взаимодействие между объектами (а также между объектом и окру- жением системы) осуществляется только при помощи обмена сообщени- ями (рис. 10.2). Сообщение моделирует взаимодействие (т.е. обмен информацией) между двумя объектами системы или между объектом и окружением сис- темы. С точки зрения системы, взаимодействие между двумя объектами разбивается на два сопряженных события: посылка сообщения одним объектом и прием сообщения другим объектом (рис. 10.2). Сообщения, приходящие из окружения системы, моделируются одним событием при- ема сообщения, а события, посылаемые в окружение, моделируются од- ним событием посылки сообщения. Сообщение имеет имя. Имя сообще- ния задает тип взаимодействия. Диаграмма может описывать несколько
обменов сообщениями с одинаковым именем. Для уникальной иденти- фикации конкретного обмена предусмотрен так называемый уникальный идентификатор обмена (message instance name), однако он используется только в текстовом представлении для снятия неоднозначности в описа- нии сопряженных событий у различных объектов. В графическом пред- ставлении такой проблемы не возникает, так как сопряженные события представляются различными концами одного и того же графического объекта (стрелки от трассы одного объекта к трассе другого). Дополнительно, язык диаграмм взаимодействия позволяет описы- вать передачу информации в сообщении (рис. 10.3). С сообщением может быть связан список параметров. Каждый параметр моделирует передачу конкретной информации от одного объекта к другому. Язык диаграмм взаимодействия не определяет семантику параметров сообщения.
MSC-диаграммы позволяют создавать более сложные описания по- ведения системы с помощью специальных операторов. В MSC’96 исполь- зуется четыре типа операторов: alt — альтернативный оператор, par — па- раллельный оператор, loop — итерация, opt — опциональная область. Графически операторные конструкции изображаются в виде прямо- угольника с пунктирными линиями в качестве разделителей. Ключевое слово оператора располагается в левом верхнем углу. Альтернативная композиция (рис. 10.4) позволяет задавать альтер- нативное выполнение секций MSC-диаграммы. Только одна альтернати- ва может быть реализована. Операция par имеет структуру, аналогичную конструкции alt (рис. 10.3), и определяет параллельное выполнение секций. Это означает, что все события внутри параллельных секций будут выполнены. Единст- венным ограничением является то, что порядок событий в каждой секции будет сохранен. Конструкция loop (рис. 10.5) имеет несколько форм. Наиболее об- щая форма — loop <n, т>, где пит — натуральные числа. Это означа- ет, что конструкция может быть выполнена от п до m раз. Вместо нату- рального числа может использоваться ключевое слово inf, обозначающее бесконечность. Оператор opt имеет структуру, аналогичную loop, но без операндов, и обозначает то же, что и оператор alt с пустой MSC в качестве второго операнда. Одним из важных понятий в MSC является условие или состояние. Состояние — это особое событие на трассе объекта. В отличие от прочих событий, одно и то же состояние может разделяться одним, двумя и более объектами. По числу объектов на диаграмме, разделяющих некоторое со-
Рис. 10.5. Цикл стояние, различают глобальные состояния (общее для всех объектов), разделяемые состояния (разделяемые несколькими, но не всеми объекта- ми) и локальные состояния (разделяемые единственным объектом). Если два объекта разделяют одно и то же состояние, то сопряженные события приема и посылки сообщений должны происходить либо оба до соответ- ствующего состояния, либо оба после соответствующего состояния. Основной недостаток стандартной MSC — невозможность описать отношения данных в параметрах сообщения. Эта проблема решается с ис- пользованием некоторого расширения стандартного MSC — mMSC (macro MSC). Основные из этих расширений включают в себя:
Макроподстановки (рис. 10.7). Они позволяют создавать множество MSC-диаграмм с одинаковой структурой и разными параметрами сооб- щения, циклами и т. п. Макроподстановки начинаются с символа # и мо- гут быть константами или функциями. Функции, кроме названия, содер- жат параметры, заключенные в скобки. Типы макроподстановок описываются в определенном файле, и на основе этой информации вместо них подставляются конкретные значения. Временные ограничения служат для указания времени посыл- ки/приема сообщения, его длительности и типа (синхронное с явно за- данным моментом выполнения, асинхронное/ Время может задаваться относительно начала работы (абсолютное), относительно предыдущего сообщения (относительное) или относительно метки. На рис. 10.8 сооб- щение М01 должно отправиться через 5 единиц времени, в течение 5 еди- ниц после получения сообщения М00 (указано с помощью метки). Применение MSC-диаграмм для описания поведения моделей Благодаря своему главному преимуществу — ясному графическому представлению, которое дает интуитивное понимание поведения описы- ваемой системы, MSC-диаграммы широко применяются для различных целей: • для определения требований; • как спецификация интерфейсов; • как спецификация взаимодействия процессов; • как базис для генерации тестов;
• для документации; • для объектно-ориентированного анализа и разработки. Хотя изначально MSC-диаграммы предназначались для описания телекоммуникационных систем, сейчас они с успехом применяются и в других областях. Обработка MSC-диаграмм В случае достаточно сложных систем могут быть обнаружены ошиб- ки, возникшие как при создании диаграммы, так и при проектировании тестируемой системы. Исправление тех же ошибок, если они будут обна- ружены на более позднем этапе жизненного цикла продукта, потребует гораздо больших затрат. Проверка MSC-диаграммы на полноту Большинство систем или их частей можно в том или ином виде представить с помощью механизма state-машин. Тогда каждому состоя- нию системы на диаграмме будет соответствовать условие — конструкция condition. Для каждого события диаграммы (под событием будем пони- мать сообщение или действие — action) можно составить множество всех состояний, которые могут непосредственно предшествовать ему (преду- словия). В этом случае проверка на полноту MSC-диаграммы будет за- ключаться в проверке того, все ли возможные случаи предусловий для ка- ждого события представлены в ней. Если это не так, то, возможно, диа- грамма не описывает полностью все возможные сценарии работы систе- мы, и множество тестов, сгенерированных по этой диаграмме, будет не- полным.
Семинар 11. Использование MS Visio для генерации MPR-файлов Разработанный набор утилит предназначен для: преобразования MSC-диаграмм в формат MPR; проверки правильности подключения сигналов; загрузки комментариев. Утилита поддерживает следующие типы конструкций: Instance Instance End Message Action Comment Coregion Text Condition Reference Block(Alt, Par, Loop, Opt). При разработке диаграмм соединение объектов осуществляется при помощи Connector Point. Если какая-либо конструкция языка не будет присоединена, то будет выдано соответствующее сообщение об ошибке, «неправильный» элемент будет выделен красным цветом, а MPR-файл генериться не будет. Исключение составляет элементы: Block, Condition, Reference и Text. Описание элементов Instance представляет собой один из взаимодействующих объек- тов. Необходимо задать имя данного элемента. После завершения приема/выдачи всех сигналов к Instance присоединяется блок Instance End. Для увеличения длины Instance TimeLine, его необходимо выделить и увеличить длину, используя Control.
[—Instance End самостоятельно не используется, применяется frat™, только совместно с конструкцией Instance. Используется для сиг- нализации того, что данный объект закончил принимать/посы- лать сигналы. -г* Message (событие/сообщение) представляет собой взаимодействие “««в* между объектами (Instances). Необходимо задать имя сообщения и параметры (если они необходимы). Messages должны быть при- соединены к Instance с помощью Connector Points. Messages без имени не обрабатываются, и выдается сообщение об ошибке. Action используется для отражения события, выполненного в рамках одного Instance. Блок необходимо присоединить с помо- щью Connector Point-а. Action без имени не обрабатывается, и выдается соответствующее сообщение об ошибке. Comment используется для написания комментариев. Его требует- ся присоединить к Connector Point на Instance. Для увеличения длины необходимо использовать Control. » I 1 Coragion Coregion показывает, что сигналы, поступившие в рамках данно- го элемента, могут приходить в разном порядке. Coregion должен быть присоединен с использованием Connector Points, в про- тивном случае возникает ошибка. ___ Text — это блок текстовых комментариев или описаний. Может С-—I располагаться в любой части рабочего листа. Condition используется для объявления события, которое рас- пространяется на несколько объектов (Instance). Может исполь- зоваться в качестве точки синхронизации. Необходимо задать имя или другие параметры. При использовании требуется «растянуть» на используемые оси Instance. Если условие Condition не распро- страняется на один из объектов (Instance), то поверх блока Condition задается Instance Line(рис. 11.1 и 11.2). Instano* Instance Line самостоятельно не применяется, а только совме- стно с блоком Condition. Для ее присоединения используем Connector Points.
Рис. 11.1. Изображение Condition без Instance Line Рис. 11.2. Изображение Condition с Instance Line Reference применяется для определения ссылки на другую диа- грамму (МPR-файл). Необходимо указать путь к используемой диаграмме. При отсутствии имени (пути) возникает ошибка. Block позволяет задавать имя блока, используется совместно с элементом Separator, который разделяет Block на 2 и более фрей- мов. В заглавии блока необходимо указать его название и параме- тры (если необходимо): Alt — указывает, что может выполняться один из фреймов в оп- ределенной последовательности. Par — указывает, что сообщения, которые были объявлены в
рамках данного блока, будут выполняться параллельно. Opt — указывает на то, что данный фрейм может использовать- ся опционально. Loop — указывает на то, что данный блок будет повторяться в ци- кле указанное число раз (указывается в параметрах). Saparator Separator самостоятельно не используется, а применяется только совместно с элементом Block. При присоединении необходимо задействовать Connector Point. Генерация MPR Для генерации требуется выполнить следующие действия: 1) Установить Microsoft Visio. 2) Создать новый документ (File->New->New Drawing). 3) Загрузить Stensil для генерации mpr файлов(РПе->Ореп ->MSC.VSS или File->Open Stensil ->MSC.VSS). Visio выдаст пре- дупреждение о том, что данный stensil содержит макросы. На пре- дупреждение следует ответить «Enable macros». 4) Нарисовать MSC-диаграмму, используя технологию Drag-And- Drop. 5) Для генерации MSC вызвать следующий макрос: Tools->Macros ->MSC->Modulel->Parse. Возможны следующие опции генерации: Current Page: При использовании данной опции генерируются толь- ко текущая страница. В указанной папке будет создан MPR-файл с именем, соответствующим имени текущей страницы в Visio. All Pages: При использовании указанной опции генерируются все страницы активного документа. Recursive Generation: При использовании данной опции имеется воз- можность генерации нескольких диаграмм на одной странице. Для
этого необходимо нарисовать диаграмму, сгруппировать ее (Shape- >Group). При этом сохраняется возможность рисовать несколько диаграмм на одной странице. По умолчанию - отключена. Allow LOST/FOUND signals: При включении данной опции разре- шается использование LOST/FOUND сигналов при проектирова- нии MSC-диаграмм. При отключении данной опции использование LOST/FOUND за- прещается, при отсутствии connect на каких-либо сигналах выдается ошибка и MPR-файл не генерируется. После генерации MPR-файла вызывается программа проверки син- таксиса сгенерированного MPR-файла. Для этого используется внешняя подпрограмма MSCJUST. Для ее использования необходимо настроить в Environmental Variables переменную SIC_PATH, которая соответствовала бы пути к соответствующей программе в пакете ТАТ. Для проверки правильности подключения сигналов необходимо вы- звать макрос Tools->Macros->MSC->Modulel->Check. В случае, если ка- кие-либо сигналы являются неподключенными, они закрашиваются красным цветом. Используется возможность загрузки комментариев, полученных на выходе тестирующей программы. Для этого необходимо вызвать следую- щий макрос: Tools->Macros->MSC->Module2->OpenMPRFile. Требуется указать путь к файлу с «трассой» тестирования. После загрузки коммента- риев с ошибкой они выделяются красным цветом и располагаются в мес- те предполагаемой ошибки. ConfigTAT Программа ConfigTAT предназначена для управления процессом ге- нерации и выполнения тестов на основе MSC-диаграмм. При активной вкладке «Test» осуществляется настройка и запуск од- ного теста. Тей | TeaSJtel Для генерации и запуска теста необходимо: • указать название проекта (от него зависит одна из строк кода Wrapper); • указать название теста (помогает различить тесты при запуске по- следовательности тестов); • указать папку проекта (в нее будут помещены файлы, необходимые для генерации теста и протоколы тестирования);
• выбрать MPR-файл, задающий тест; • выбрать файл конфигурации, описывающий методы тестируемой модели; • выбрать файлы тестируемой модели; • указать путь к Wrapper-y (интерфейс между моделью и тестом). Project Name Test Name Project Path Указать следующие настройки. Для макропроцессора: • раздел в файле конфи- гурации (конфигурации для нескольких тестов могут находиться в од- ном файле); • inline loops with counter equal to <1> into plain code — преобразовывать циклы с одной итераци- ей в линейный код; • unroll loops with constant number of iterations — Sff'ryt macroprc I atsgen | template | V , • Section in configuration fie lde,ault include He Browse | Г inline loops with colter equal to <1> into plain code Г untoS loops with constant number of iteration* Г~ exclude time specification* m generated files Set to default I Set ALL to default разворачивать циклы с заданным числом итераций в линейный код; • exclude time specifications in generated files — исключать временные спецификации из сгенерированных файлов.
Для генератора Abstract Test Suite: • active events in start — первый сигнал от теста к модели; • passive events in start — первый сигнал от модели к тесту. Настройки macroprc atsgen } temptate | Р active events in start Г~ passive events in^tart Set to default Set ALL to default Для шаблона генерации теста на С#: • generate break after first error detection — завершать тест после первой же ошибки; • generate instance check func- tionality — проверять, соответ- ствуют ли отправитель и по- лучатель сигнала требуемым; • create log file of generation process — протоколировать процесс генерации теста; • generate time processing func- tionality — проверять времен- ные требования к системе. Группа кнопок «Run» служит для Настройки macroprc | atsgen template | Р generate break after first error detection Г* generate instance check functionaSty P create log file of generation process P generate time processing functionality t О -7 I ’ £ Set to default Set ALL to default I запуска по отдельности или цели- ком этапов генерации теста и выполнения сгенерированного теста: • кнопка «macroprc» запускает макро- процессор; • кнопка «atsgen» запускает Abstract Test Suite генератор; • кнопка «template» запускает шаблон генерации теста на языке С#; • кнопка «С# compiler» запускает ком- пилятор языка C# с получением на вы- ходе файла test.exe, который представ- ляет собой готовый к запуску тест; • кнопка «Generate whole test» последо- вательно запускает макропроцессор, Abstract Test Suite генератор, шаблон генерации теста на языке C# и компи- лятор языка С#;
• кнопка «Run test» осуществляет запуск теста. Во время выполне- ния теста изменение всех настроек блокируется и отображается Progress Ваг. Выполнение теста можно прервать нажатием кнопки «Cancel»; • кнопка «Run ALL» осуществляет последовательно генерацию и запуск теста; • кнопка «Delete temporary files» удаляет промежуточные файлы, созданные в процессе генерации теста. Группа «Test Logs» позволяет просматривать протоколы тестирова- ния: • при нажатии кнопки «HTML log» в Internet Explorer отображается протокол тестирования в виде html-страницы; • при нажатии кнопки «Text log» в notepad отображается протокол тестирования в виде txt-файла; • в listBox-e «MPR logs» отображается список протоколов в формате трг (от- дельный протокол для каждого testcase- а и каждой итерации теста), которые можно открыть в программе Telelogic нажатием кнопки «View»; • с помощью кнопки «Delete logs» можно удалить все протоколы тестирования. В richTextBox-e в правой части формы отображается информация о процессе генерации и выполнении тестов. Очистить richTextBox можно с помощью кнопки «Clear»:
—Running Macropreprocessor- - Macro Processor - version 2 1 for Win2K build 17 COPYRIGHT 2001-2002 Generation NET, ALL RIGHTS RESERVED Regntred to Generation NET L^/354-04(vpk^ics2 ecd spbstu,iu), host 354> 04* EARNING .nd no micros defined —Running ATS Generator— Abstract Test Generator - version2.1 for Win2!Cbuild 17 COPYRIGHT 2001-2002 Generation .NET, ALL RIGHTS RESERVED Regutredto Generation NET Lab/354-O4(vpk(®ics2 .ecd spbstu.ni), host *354- °* —Running Code Generation Template for ОИ— • Code Generation Template for C# - version 1.0 COPYRIGHT 2003 GENERATION NET, ALL RIGHTS RESERVED WARNING: ats. no function is defined for dumping type 'byte1 WARNING ats no function is defined for damping type %yte‘ WARNING ats no function is defined for damping type ЪооГ WARNING ats no function is defined for damping type ЪооГ WARNING ats no function is defined for damping type ЪооГ WARNING ats: no function is defined for dumping type ^yte' WARNING ats no function is defined for dumping type ‘long1 : WARNING ats no function v defined for dumping type 'long* WARNING ats no function is defined for dumping type ЪооГ -Running C# Compiler— w| □ear Конфигурации тестов можно сохранять и открывать с помощью ко- манд, соответственно, Save и Open меню File:
При активной вкладке «TestSuite» осуществляется на- Teasufte] стройка и запуск набора тестов. Кнопки «Add» и «Remove» позволяют добавлять и убирать отдельные тесты (файлы конфигурации, созданные на вкладке «Test») из набора тестов. Кнопка «Run» запускает на выполнение по- следовательность тестов. При этом все кнопки и меню блокируются и отображается Progress Ваг. Остановить выполнение данного теста или всей тестовой последова- тельности можно с помощью кнопок «Cancel» и «Cancell ALL» соответст- венно. Runniriq test 3 of 4 Cancel Cancel ALL В richTextBox-e в правой части формы отображается краткая инфор- мация о результатах тестирования: Warehouse!estl - Тest Finished Successfully Warehouse!est2 - Test Finished Successfully Warehouse!est3 - Test Finished Successfully Warehouse! est4 -! est Finished Successfully Warehouse!est5 -!est Finished Successfully Warehouse!estS -!est Finished Successfully Warehouse!est7 ERROR: !imeout error ' Warehouse!est8 ERROR !imeout error Warehouse!estS - ERROR !imeout error Warehouse!estl 0 -! est Finished Successfully Warehouse!estl 1 - ERROR !imeout error Warehouse!estl2 ERROR !est Exited Abnormallyfempty log] С помощью пунктов «Open» и «Save» меню «File» можно, соответст- венно, загрузить и сохранить список тестов тестового набора. SysLog Animator Manual Эта программа предназначена для визуализации журнала системы, полученного в результате тестирования системы). Журнал системы представляет собой набор строчек следующего вида: 08.09.2003 15:01:29 : СИСТЕМА: Запрошен статус СКЛАДА 08.09.2003 15:01:29 : СКЛАД: Статус СКЛАДА = 16 08.09.2003 15:01:29 : СИСТЕМА: Запрошены параметры оси 08.09.2003 15:01:30 : СИСТЕМА: СКЛАДУ послана команда ПОЛО-
ЖИТЬ В РЕЗЕРВ: 4 10 2-1-1-1 08.09.2003 15:01:30: ТЕРМИНАЛ ОСИ : 1 - нет данных 08.09.2003 15:01:34: СКЛАД : 0 - команда ПОЛОЖИТЬ В РЕЗЕРВ успешно принята 08.09.2003 15:01:38: СИСТЕМА : Запрошено сообщение от СКЛАДА 08.09.2003 15:01:38: СКЛАД : 1 - ПОДШИПНИК успешно ПРИ- НЯТА 102-1-1-1 08.09.2003 15:01:38: СИСТЕМА: Запрошен статус СКЛАДА 08.09.2003 15:01:38: СКЛАД : Статус СКЛАДА = 16 Каждая строчка такого вида формируется в кадр. Кадр — набор дей- ствий, происходящих в рамках одного события. Внешний Вид Приложения Главное Меню File — Состоит из двух подпунктов: Open — Открыть журнал сообщений системы. Exit — Выйти из приложения. Не Options Help Options — Состоит из одного подпункта: Config — Позволяет настроить толщину линий, обозначающих сигналы и задержку при анимации сигналов Help — Состоит из одного подпункта: About — Информация о текущей версии Log Animator-a
File->Open При выборе пункта File->Open откроется диалоговое ок- । Ореп ~ । но, в котором можно выбрать файл, содержащий журнал сооб- щений системы, выделив необходимый файл среди имеющихся и нажав кнопку. Если отказаться от открытия файла, необходимо нажать cancel клавишу File->Exit Для выхода из приложения можно выбрать пункт меню Exit или нажать кнопку «х» в правом верхнем углу окна. (.Ffe Options Орел Ь F «
Options->Config... Для настройки параметров визуализации нужно зайти в меню Options и выбрать подпункт Config...: | Options Help В результате появляется диалоговое окно вида: Arrows Width — Устанавливает толщину линий, обозначающих сигнал. Минимальное значение = 1. При этом линия выглядит следующим образом: Максимальное значение = 5. При этом линия выглядит следующим образом: Default значение = 2. При этом линия выглядит следующим образом: Arrows Time Delay — Устанавливает время вывода сигналов. Чем больше значение параметра, тем дольше будет выводиться пунктирная линия, обозначающая сигнал. Минимальное значение = 20. Максимальное значение = 200. Default-значение = 50. Кнопка Cancel позволяет отказаться от изменения параме- тров и вернуться в визуализатор. При нажатии кнопки Default-значения принимают вели- чины, предопределенные заранее. При нажатии кнопки ОК система устанавливает новые значения. Cancel .__а___ Default °С1
Options- > Debug Выбрав этот пункт меню, можно настроить вариант работы прило- жения. Если выбран режим Debug, то приложение будет выво- дить сообщения в отладочном режиме. Если Debug не выбран, то сообщения будут выводиться в обычном формате: Help->About Для просмотра информации о текущей версии проекта необходимо зайти в меню Help и выбрать подпункт About: Появится диалоговое окно следующего вида: Config... Debug About... SysLog Animator vl .0 RCi DirectX 9.0 Version Bufcj OS September 2003 Generation.NET (t) 2003 OK | Для выхода из диалогового окна необходимо нажать кнопку ОК. Анимирование кадров Если открыть файл, содержащий журнал сообщений системы, то по- явится разбитый на кадры журнал. Любой кадр может быть выбран путем нажатия двойного щелчка мышки на названии нужного кадра. iHi TFM-Д -1апрошен статус СКЛАДА СКЛАД: Статус СКЛАДА - 32 СИСТЕМА Запрошены параметры подшипника ТЕРМИНАЛ ПОДШИПНИКА : 0 - параметры возвращены СИСТЕМА: Запрошены параметры оси : ТЕРМИНАЛ ОСИ : 1 нет данных СИСТЕМА: СКЛАДУ послана команда ПОЛУЧИТЬ ИЗ ВХО , СКЛАД: 0 - команда ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ у< j СИСТЕМА Запрошено сообщение от СКЛАДА СКЛАД 1 - ПОДШИПНИК успешно ПРИНЯТ St-0 СоМО Rov- СИСТЕМА Запрошен статус СКЛАДА СКЛАД Статус СКЛАДА - 32 СИСТЕМА: Запрошены параметры подшипник ТЕРМИНАЛ ПОДШИПНИКА. О параметры возвращены —• СИСТЕМА Запрошены параметры оси СИСТЕМА СКЛАДУ послана команда ПОЛУЧИТЬ ИЗ ВХО ТЕРМИНАЛ ОСИ 1 нет данных СКЛАД 0 - команда ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ ус СИСТЕМА Запрошено сообщение от СКЛАДА СКЛАД : 1 - ПОДШИПНИК успешно ПРИНЯТ St-0 Col-1 Ron СИСТЕМА : Запрошен статус СКЛАДА СКЛАД: Статус СКЛАДА - 32 СИСТЕМА: Запрошены параметры подшипника ТЕРМИНАЛ ПОДШИПНИКА: 1 - нет данных СИСТЕМА: Запрошены параметры оси ТЕРМИНАЛ ПСИ 0 - папаметпк| вглапашвны 1212 1J 111_____________________J 2J
Изображение начального состояния выбранного кадра появится справа от списка кадров. вход ВХОД Терминал Оси СКЛАД Выходная Ячейка Входная Ячейка Терминал Подвипника ВЫХОД Система Для работы с кадрами существует панель управления: Для выбора нужного кадра можно воспользоваться кнопками пере- мотки: « | — выбрать первый кадр; — выбрать предыдущий кадр; — выбрать следующий кадр; » | — выбрать последний кадр. Для проигрывания кадра/кадров можно воспользоваться следующи- ми двумя клавишами: piayi | — проиграть 1 кадр; play all ~ непрерывно проигрывать все кадры, начиная с текущего и до финального. В любой момент можно прервать проигрывание, ST0P । нажав кнопку Stop, которая появится во время проигрывания вместо кнопки Play All. Последним в списке кадров является финальное состояние (ФС).
Финальное состояние отражает состояние системы после выполне- ния последнего кадра. ФС нельзя проиграть, т.к. ФС не является кадром, а лишь отражает состояние системы в результате произошедших в системе событий. . Здесь первая цифра обозначает номер текущего кадра, вторая — сколько всего имеется кадров. Функционально-графическая составляющая Сигналы Сигналы в системе бывают четырех видов: • сигнал синего цвета означает, что идет запрос от системы; • сигнал красного цвета означает, что ответом на запрос системы является ошибка; • сигнал желтого цвета означает, что идет ответ на запрос системы, не содержащий никаких данных; • сигнал зеленого цвета означает, что запрос системы выполнен ус- пешно. Ответ положительный. Объекты — ось; — подшипник; — манипулятор. Ось У оси существуют два параметра, по которым к ней подбираются подшипники: • передний диаметр; • задний диаметр. 12-J2 — здесь параметры переднего и заднего диаметров равны 12. X ? — здесь значение переднего диаметра не соответствует допусти- мому по FS, а значение заднего диаметра не определено.
о в е Подшипник Подшипник может быть изображен в трех вариантах: — означает, что у подшипника не определен номер группы. — означает, что у подшипника 12 номер группы. — означает, что номер группы у подшипника не соответствует до- пустимому по FS.
Приложение 1. Руководство по подготовке компьютерного класса Требования к аппаратному и программному обеспечению Необходимое аппаратное обеспечение Intel Pentium 3 — 600 MHZ 128 Mb RAM 50 Mb Free Disk Space Desktop Resolution 1024x768 or Higher Desktop Colors Depth 16 Bit or Higher Необходимое программное обеспечение Windows 2000/XP Visual Studio .NET MS SQL Server 2000 ActiveTCL 8.0.3 or Higher Ms Visio 2000 or Higher Acrobat Reader 4 or Higher Руководство по инсталляции Запустите программу инсталляции. Появится диалоговое окно. i” I. >in.q I*, act и е Ii»5» .iWsliw Id Wizard Welcome to the Inst iNstiMd Wizard for Testing Practice The InstalShteld(R) Wizard wii test al Testing Practice on your computer. To continue, dick Next WARNING This program is protected by copyright law and international treaties. Ptease forward comments and questions tovpk®ics2 ecd.spbstu.ru TEL: 7(812)552=6191 Для продолжения инсталляции нужно нажать кнопку NEXT. После этого необходимо ввести путь, куда будет установлено прило- жение.
!.. л Testing Practice InstaflShteki Wizard Destination Folder Cfck Next to instat to the folder, or dick Change to install to a dfferent folder Install Tasting Practice to: C:V>rog«riRlesiGeoeratlon.№TYtestlng Practical Следующий шаг дает информацию о пути и необходимом объеме па- мяти. -FeSureWSW^rja- CWr on an кап In the 1st beta* to Change how a feature is totaled. rtasfeaturereqceresOSZSKBon /our hard drive. Instalto: C:\Prcvam 1Чее\вопогаСКп.АЕТ\Те<ИпдРгасйсе\ gangs... | :a [nsWIShield— —---------...............—.....—----------------.—-------------—... trt> | Space | <ftack [ fr*t> | Cancel Следующее диалоговое окно предлагает проверить правильность введенной информации. Если путь набран правильно, следует нажать кнопку Install.
После этого будет выполняться процесс установки приложения. Продолжение процесса установки отображается с помощью про- гресс-бара. В случае возникновения сообщения вида: The operating «/stem b not presently tonftared to run thb enofceaon. Нажмите кнопку OK. После завершения копирования необходимых файлов появится ди- алоговое окно: Здесь требуется указать параметры SQL-сервера. ВНИМАНИЕ! В случае ошибочного указания параметров сервера придется повторить инсталляцию заново.
В случае возникновения сообщения следующего вида SQL Server Service Control Manager Utility -Server -Pwd Service Control Maiager UtatyCommend Une rguments *Ц»1 I «Start,2-Restart^ 3-lsRimnrs(returnsextendsof4(FALSE, IfTRUE) »-Delete Serve», S-metal Service, 6—Stop, 7-Modfy «Optional name of the SQL server (defaut is this machine) «Optional SQl Server 'sa' password (defaUt Is ”) , «Optional spece-delefted 1st of server startup optlore -Service •ExePath SvcStartType -SvcAccount -Swpwd -Dependencies «Name of the service (default Is MSSQISwver) «Poth to the service exe on ths machine* <1 for SERVICE J)€MaM>_START Or 2 for SERVICER -\_5TART> «service account name» «service account name password* «List of service name dsoendenees separated by seed-colons* необходимо нажать кнопку OK. При появлении сообщения: Stencil contehs macros. Mu rosolt Visio J I Macros may contain viruses, it Is always safe to rbsable macros, but t the macros are togltknate, you еДО lose some functionality [C feeble о?*„_ I Enable Macros | tdpreSnfo | Необходимо нажать клавишу Enable Macros. Появится следующее окно: После нажатия кнопки Finish инсталляция завершится. В результате должна появиться директория Practice, содержащая папку Documents. Файлы четырех документов MSTesting, Практи- KyMMSTesting, ПрактикумРБ и ПрактикумНЬО, находящиеся на инстал-
ляционном носителе рядом с файлом Set up, необходимо скопировать в папку Practice/Documents, после чего система будет готова к работе. Если документы в папке Documents сохранены в формате .doc, можно исполь- зовать все ссылки в любом документе без изменений. Если документы со- хранены в формате .pdf, необходимо заменить все ссылки в документах с (../path...) на (Practice/Documents/path...). Проверка инсталлированной системы Для проверки правильности установленного пакета необходимо за- пустить файл run.bat, находящийся в составе инсталляционного пакета в папке SystemTesting/ScriptTests. После завершения тестирования, когда исчезнет консольное окно, требуется запустить Пуск->Программы->Те81 Practice-> SysLogAnimator. В нем следует открыть log-файл warehousetest0001system.log, находя- щийся в SystemTesting/ScriptTests/Logs, нажать PLAY ALL и наблюдать последовательность состояний и действий системы, зафиксированную в log-файле в процессе тестирования. Важное замечание: В папке с тестами может находиться несколько файлов с расширением .log. Только файл, имя которого содержит суф- фикс system (структура имени — *system.log), пригоден для просмотра в SysLogAnimator.
Приложение 2. Функциональная спецификация Введение Назначение Система предназначена для управления автоматизированным комп- лексом хранения подшипников. Она обеспечивает прием подшипников на склад, а также подбор и выдачу по запросу. Данный документ описывает требования и ограничения на исполь- зование приложения. Принятые сокращения В настоящем документе приняты следующие определения и сокра- щения: Сокращение Определение FS Данный документ. Содержит технические требования, предъявляемые к программному продукту HLD High Level Design. Содержит описание модульной структуры проекта и взаимодействия его модулей Проект Проект системы управления автоматизированным комплек- сом хранения подшипников В последующем тексте слово «должен» определяет необходимое тре- бование к продукту. Слова «может», «предполагает» и «способен» опреде- ляют направление работ, которое подлежит дальнейшему уточнению. Обзор Введение Комплекс хранения подшипников состоит из: 1. Склада (п. 1.2.2). 2. Терминала подшипника (п. 1.2.3). 3. Терминала оси (п. 1.2.4). У каждого из элементов комплекса существует программа управле- ния, реализованная в виде в dll, принимающая на вход высокоуровневые команды и преобразующая их в управляющие воздействия для данного элемента.
Управляет переферийными устройствами и взаимо- — Хранит информацию о подшипниках на стеллажах, о выданных подшипниках, о измеренных осях MS SQL Server 2000 MS Windows 2000 Рис. П-2.1. Структура комплекса Участок сборки колесной пары Производит измерение параметров оси ТЕРМИНАЛ ОСИ По запросу от "ТЕРМИНАЛА ОСИ" подшипники выдаются со "СКЛАДА" Ось с подобранными и одеиыми на нее подшипниками КОЛЕСНАЯ ПАРА Подшипник Принимает, выдает и хранит подшипники Рис. П-2.2. Концептуальная схема стеллажа У ТЕРМИНАЛ ПОДШИПНИКА Производит измерение параметров подшипника
Склад Склад предназначен для хранения подшипников. Он представляет собой стеллаж с ячейками, имеет входную и выходную ячейку и робот-манипулятор. Все ячейки имеют координаты — сторона стеллажа (0 или 1), ряд (О или 1 для каждой стороны), колонка (от 0 до 4 для каждого ряда), а так- же порядковый номер. Выходная ячейка имеет номер 999 и координаты (9,9,9), а приемная — 0 и координаты (0,0,0). Всего ячеек, кроме прием- ной и выходной, 20 штук. Стеллаж имеет две области ячеек: рабочую и резервную (рис. П-2.2). В рабочей области хранятся опознанные подшипники, т.е. подшипники, для которых были получены параметры с терминала подшипника и опе- рация по перемещению в ячейку завершилась успешно. В резервную об- ласть попадают неопознанные подшипники, т.е. подшипники, при опе- рациях с которыми произошел сбой и нельзя гарантировать достовер- ность полученных для них параметров. Как в рабочей, так и в резервной областях, могут существовать сбой- ные ячейки. Ячейка помечается как сбойная, если робот-манипулятор не может взять из нее подшипник или если он не может положить под- шипник в эту ячейку. Каждая ячейка может быть помечена либо как за- нятая, либо как свободная, либо как сбойная. О сч m tj- сторона 0 сторона 1 Рис. П-2.3. Схема стеллажа Статус склада Склад может иметь в каждый момент времени один из следующих статусов:
Табл. П-2.1. Статус склада Код Значение 32 Подшипник во входной ячейке 16 Подшипник в манипуляторе 4 Нет нуля 0 Склад свободен О Склад свободен — нормальное состояние склада 4 Нет нуля — ошибочное состояние Рис. П-2.4. Граф переходов статусов склада Список команд складу Склад принимает следующие команды: Табл. П-2.2. Список команд складу № Код Название Полное название команды 1 1 GetR ПОЛУЧИТЬ ИЗ ВХОДНОЙ ЯЧЕЙКИ 2 2 SendR ОТПРАВИТЬ ИЗ ЯЧЕЙКИ В ВЫХОДНУЮ ЯЧЕЙКУ 4 4 PutR ПОЛОЖИТЬ В РЕЗЕРВ 5 6 SetN ПРОИЗВЕСТИ ЗАНУЛЕНИЕ 7 20 Term ЗАВЕРШЕНИЕ КОМАНД ВЫДАЧИ
Формат команд складу Команды имеют формат: Табл. П-2.3. Формат команд складу № Параметр Тип Диапазон допус- тимых значений Описание 1 NameCommand int 1...6,9,10,20 Название команды (табл. П-2.2) 2 TagSt int 0...1 Сторона результирующей ячейки 3 TagCol int 0...4 Колонка результирующей ячейки 4 TagRow int 1...2 Ряд результирующей ячейки 5 SourseSt int 0...1 Сторона исходной ячейки 6 SourseCol int 0...4 Колонка исходной ячейки 7 SourseRow Int 1...2 Ряд исходной ячейки Табл. П-2.4. Сообщения от склада в ответ на посылку команды № Код Описание № Код Описание 1 -4 Нет свободных ячеек 5 0 Успешное получение команды 2 -3 Не послать 6 1 Ошибка при получении команды 3 -2 Тайм-аут 7 2 Склад не понял команду 4 -1 Нет клиента 8 3 Склад занят Табл. П-2.5. Сообщение от склада № Код Описание 1 -1 Нет склада 2 0 Нет сообщения 3 1 Команда выполнена без ошибки 4 2 Команда выполнена с ошибкой. Не удается взять подшип- ник из заданной ячейки 5 3 Команда выполнена с ошибкой. Не удается положить под- шипник в заданную ячейку
Табл. П-2.6. Статус обмена со складом » № Код Описание 1 -3 Нет обмена 2 -2 Тайм аут 3 -1 Нет клиента 4 0 Возвращаемый параметр mParametr содержит статус склада 5 1 Нет данных Терминал подшипника Терминал подшипника производит измерение параметров подшип- ника, и на запрос системы возвращает одно из следующих сообщений. Табл. П-2.7. Статус обмена с терминалом подшипника № Код Описание 1 -3 Нет обмена 2 -2 Тайм-аут 3 -1 Нет клиента 4 0 Структура с измеренными параметрами подшипника в буфере 5 1 Нет данных Если код сообщения = 0, то в буфере находится структура с параметрами: Табл. П-2.8. Параметры подшипника № Параметр Тип Диапазон допустимых значений Описание 1 Name Master String Любая строка длиной до 255 символов ФИО мастера, производив- шего измерения 2 Factory String Любая строка длиной до 255 символов Название депо 3 ShiftNum Byte 1...2 Номер рабочей смены 4 Number String Любая строка длиной до 255 символов Номер подшипника 5 GroupNum Int 12...20 Номер группы подшипника 6 Septype byte 0...1 Тип сепаратора подшипника 7 AShift Float 0.01...1 Осевой сдвиг
Терминал оси Терминал оси производит измерение ее параметров. В ответ на за- прос системы он возвращает одно из следующих сообщений: Табл. П-2.9. Статус обмена с терминалом оси № Код Описание 1 -3 Нет обмена 2 -2 Тайм аут 3 -1 Нет клиента 4 0 Структура с измеренными параметрами оси в буфере 5 1 Нет данных Табл. П-2.10. Параметры оси № Параметр Тип Диапазон допустимых значений Описание 1 Name Master String Любая строка длиной до 255 символов ФИО мастера, производившего измерения 2 ShiftNum Byte 1...2 Номер рабочей смены 3 Factory String Любая строка длиной до 255 символов Название депо 4 ANumber Int Любая строка длиной до 255 символов Номер оси 5 Side Byte 0...1 Сторона оси 0-правая, 1-левая 6 BackDiam Float 12...20 Посадочный диаметр задний 7 Front Diam Float 12...20 Посадочный диаметр передний
Интерфейсы взаимодействия системы Рис. П-2.5. Интерфейсы системы Интерфейс со складом (Store.dll) Для получения сообщения от склада: static public long GetStoreMessage() — возвращаемые значения описаны в табл. П-2.6. Для получения статуса склада: static public int GetStoreStat (out long mParametr) — возвра- щаемые значения описаны в табл. П-2.5. Значения, принимаемые возвращаемым параметром mParametr, описаны в табл. П-2.1. Для посылки команды складу: static public int SendStoreCom (int NameCommand, int TagSt, int TagCol, int TagRow, int SourseSt, int SourseCol, int SourseRow) — возвращаемые значения в передаваемые парамет- ры описаны в табл. П-2.3. Интерфейс с терминалом подшипника (Bearing.dll) Для получения сообщения от терминала подшипника система долж- на вызвать следующую функцию модуля Bearing.dll: static public int GetRollerPar(out string NameMaster, out string Factory, out string Number, out byte ShiftNum, out int GroupNum, out byte SepType, out float AShift). Функ-
ция должна возвращать одно из значений, перечисленных в табл. П- 2.7. Возвращаемые параметры описаны в табл. П-2.8. Интерфейс с терминалом оси (Axle.dll) Для получения сообщения от терминала оси система должна вызы- вать следующую функцию модуля Axle.dll: static public int GetRollerPar(out string NameMaster, out string Factory, out string Number, out byte ShiftNum, out int GroupNum, out byte SepType, out float AShift). Функ- ция должна возвращать одно из значений, перечисленных в табл. П- 2.9. Возвращаемые параметры описаны в табл. П-2.10. Специфические требования Система управления комплексом должна: Произвести опрос статуса склада (вызвать функцию GetStoreStat). Добавить в журнал сообщений запись «СИСТЕМА: Запрошен статус СКЛАДА». В зависимости от полученного значения произвести следую- щие действия: Полученный статус склада = 32. В приемную ячейку склада поступил подшипник. Система должна: Добавить в журнал сообщений запись «СКЛАД: Статус СКЛАДА = 32». Получить параметры поступившего подшипника с терминала под- шипника (должна быть вызвана функция GetRollerPar). Добавить в журнал сообщений запись «СИСТЕМА: Запрошены па- раметры подшипника». В зависимости от статуса терминала подшипника (возвращенного функцией GetRollerPar значения) должны быть выполнены действия, приведенные в табл.: Полученный статус склада = 16. Склад свободен, т.е. не выполняет никаких команд, но при этом в манипуляторе находится подшипник. В этом случае система должна: Добавить в журнал сообщений запись «СКЛАД: Статус СКЛАДА = 16». Поставить на первое место в очереди команду PutR — «ПОЛОЖИТЬ В РЕЗЕРВ». Полученный статус склада = 4. Нет нуля. В этом случае складу сис- тема должна: Добавить в журнал сообщений запись «СКЛАД: Статус СКЛАДА — 4». Поставить на первое место в очереди команду SetN — «ПРОИЗВЕ- СТИ ЗАНУЛЕНИЕ». Полученный статус склада = 0. Склад свободен. Никаких действий в ответ на этот статус система предпринимать не должна.
ТЬбл. П-2.11 Действия в зависимости от статуса терминала подшипника № Статус терминала подшипника Действие системы 1 -3 Добавить в журнал сообщений запись «ТЕРМИНАЛ ПОДШИПНИКА: -3 - нет обмена» 2 -2 Добавить в журнал сообщений запись «ТЕРМИНАЛ ПОДШИПНИКА: -2 - таймаут» 3 -1 Добавить в журнал сообщений запись «ТЕРМИНАЛ ПОДШИПНИКА: -1 - нет клиента» 4 0 Добавить на первое место команду GetR — «ПОЛУЧИТЬ ИЗ ПРИЕМНИКА В ЯЧЕЙКУ» Добавить в журнал сообщений запись «ТЕРМИНАЛ ПОДШИПНИКА: 0 - параметры возвращены <Номер_группы>» 5 1 Добавить в журнал сообщений запись «ТЕРМИНАЛ ПОДШИПНИКА: 1 - нет данных» 6 Другое Добавить в журнал сообщений запись «ТЕРМИНАЛ ПОДШИПНИКА: ОШИБКА: Неопределенный статус» При любом другом статусе в журнал должно быть добавлено сообще- ние «СКЛАД: ОШИБКА: Неопределенный статус». Произвести опрос терминала оси (вызвать функцию получения со- общения от терминала — GetAxlePar). В журнал сообщений должно быть добавлено сообщение «СИСТЕМА: Запрошены параметры оси». В зави- симости от статуса терминала оси (возвращенного функцией GetAxlePar значения) должны быть выполнены следующие действия: При поступлении команды в очередь система должна отправить ко- манду на выполнение складу (параллельно с продолжающимся опросом терминала оси) и в зависимости от возвращенного функцией посылки ко- манды статуса команды, выполнить следующие действия (табл. П-2.4): Полученный статус: 0 — успешное получение команды. В журнал сооб- щений дложно быть добавлено сообщение «СКЛАД: 0 — команда <Пол- ное_название_команды> успешно принята». Команда должна быть удале- на. Система должна получить сообщение от склада о результатах выпол- нения команды. Полученный статус: 1 — при посылке команды произошла ошибка. В журнал сообщений дложно быть добавлено сообщение «СКЛАД: 1 — ошибка при посылке команды <Полное_название_команды>». Команда должна быть удалена.
Табл. П-2.12. Действия, зависящие от статуса терминала оси № Статус терминала Действие системы подшипника 1 -3 Добавить в журнал сообщений запись «ТЕРМИНАЛ ОСИ: -3 — нет обмена» 2 -2 Добавить в журнал сообщений запись «ТЕРМИНАЛ ОСИ: -2 — таймаут» 3 -1 Добавить в журнал сообщений запись «ТЕРМИНАЛ ОСИ: -1 — нет клиента» 4 0 Добавить в журнал сообщений запись «ТЕРМИНАЛ ОСИ: 0 — параметры возвращены <Передний_диаметр> < Задний_диаметр>» Подобрать два подшипника из имеющихся на складе в со- ответствии со следующими требованиями: Они должны находиться в ячейках с разными номерами Разность группы первого подшипника и группы передне- го посадочного диаметра оси (FrontDiam) должна быть меньше либо равна 2 Разность группы второго подшипника и группы заднего посадочного диаметра оси (BackDiam) должна быть мень- ше либо равна 2 Разность разностей пункта ii и iii должна быть меньше ли- бо равна 2 Выдаваемые подшипники должны иметь одинаковый тип сепаратора. В первую очередь должны выдаваться подшипники, кото- рые находятся на складе дольше всего. При успешном подборе подшипников: В журнал сообщений должно быть добавлено сообщение «ТЕРМИНАЛ ОСИ: ПОДШИПНИКИ подобраны» ' В конец очереди команд должны быть добавлены две ко- манды «SendR Отправить ячейку на выход» с параметрами подобранных подшипников, а также завершающая выдачу команда «Тепл Завершение команд выдачи» При отсутствии на складе подшипников, удовлетворяю- щих заданным параметрам, в журнал должно быть добав- лено сообщение «ТЕРМИНАЛ ОСИ: Не подобрать ПОД- ШИПНИКОВ». 5 1 Добавить в журнал сообщений запись «ТЕРМИНАЛ ОСИ: 1 — нет данных» 6 Другое Добавить в журнал сообщений запись «ТЕРМИНАЛ ОСИ: ОШИБКА: Неопределенный статус»
Полученный статус: 2 — склад не понял команду. В журнал сообщений должно быть добавлено сообщение «СКЛАД: 2 — склад не понял команду <Полное_название_команды>». Система должна попытаться послать ко- манду повторно; после второй неудачной попытки в журнал должно быть добавлено сообщение «СКЛАД: Не понял команду после двух попыток», команда должна быть удалена, а система должна приступить к выполне- нию следующей в очереди команды, если очередь не пуста. Полученный статус: 3 — склад занят. Система должна посылать ко- манду в течение 30 секунд, при истечении данного интервала в журнал должно быть добавлено сообщение об ошибке «СКЛАД: 3 — занят более 30 секунд», команда должна быть удалена, а система должна приступить к выполнению следующей в очереди команды, если очередь не пуста. Полученный статус: -1 — нет склада. Система должна попытаться выполнить команду повторно; после второй неудачной попытки в журнал должно быть добавлено сообщение «СКЛАД:-1 — нет СКЛАДА» и систе- ма должна завершить работу. Полученный статус: -2 — таймаут. Система должна попытаться вы- полнить команду повторно; после второй неудачной попытки в журнал должно быть добавлено сообщение «СКЛАД:-2 — таймаут при посылке команды» и система должна завершить работу. Полученный статус: -3 — не посылать. Система должна попытаться выполнить команду повторно; после второй неудачной попытки в журнал должно быть добавлено сообщение «СКЛАД:-3 — не удается послать ко- манду СКЛАДУ» и система должна завершить работу. Полученный статус: -4 — нет свободных ячеек. В журнал должно быть добавлено сообщение «СКЛАД:-4 — нет свободных ячеек», команда должна быть удалена, а система должна приступить к выполнению следу- ющей в очереди команды, если очередь не пуста. При получении любого другого статуса в журнал должно быть доба- влено сообщение «СКЛАД: ОШИБКА: Неопределенный статус при по- сылке команды». После успешного получения складом команды (получения статуса команды = 0) система должна получить сообщение от склада и добавить в журнал сообщение «СИСТЕМА: Запрошено сообщение от СКЛАДА»: Полученное сообщение: -1 — нет склада. Система должна попытать- ся получить сообщение повторно, после второй неудачной попытки в журнал должно быть добавлено сообщение «СКЛАД:-! — нет СКЛАДА» и система должна завершить работу. Полученное сообщение: 0 — нет сообщения. Это означает, что склад еще занят, следует продолжать опрос. Полученное сообщение: 1 — Команда выполнена без ошибки. На первое место в очередь должна быть добавлена команда с параме-
трами удаленной команды, но номер результирующей ячейки в команде должен быть заменен номером другой свободной ячейки, соответственно алгоритму поиска свободной ячейки. Полученное сообщение: 2 — Команда выполнена с ошибкой, не удает- ся взять подшипник. Если текущая выполняемая команда: GetR — «Получить из приемника в ячейку»: В журнал должно быть добавлено сообщение «СКЛАД: ОШИБКА: Не взять из входной ячейки». Текущая команда должна быть удалена. На первое место в очереди должна быть добавлена команда SetN — «Произвести зануление». SendR — «Отправить ячейку на выход»: В журнал должно быть добавлено сообщение «СКЛАД: Не взять из ячейки <Номер_ячейки>». Текущая команда должна быть удалена. На первое место в очереди должна быть добавлена команда SetN — «Произвести зануление». На второе место в очереди должна быть добавлена команда SendR — «Отправить ячейку на выход» с параметрами удаленной в п. 2 команды. При получении того же статуса при повторной попытке выполнения команды SendR: Ячейка должна быть помечена как сбойная (не должны предприни- маться дальнейшие попытки положить в нее подшипники) В журнал должно быть добавлено сообщение «СИСТЕМА: Ячейка <Номер_Ячейки> <Номер_Стороны> <Номер_Колонки> <Номер_Ряда> помечена как сбойная» Текущая команда SendR должна быть удалена. На первое место в очередь должна быть добавлена команда с параме- трами удаленной команды, но номер результирующей ячейки в команде должен быть заменен номером другой свободной ячейки, соответственно алгоритму поиска свободной ячейки. Полученное сообщение: 3 - команда выполнена с ошибкой, не удается по- ложить подшипник. Если порядковый номер, результирующей ячейки — 999, т.е. это выходная ячейка, то: В журнал должно быть добавлено сообщение «СКЛАД: Не могу по- ложить подшипник в выходную ячейку». Текущая команда должна быть отложена. Должна быть выполнена команда SetN — «Произвести зануление» Должна быть предпринята попытка выполнить отложенную коман- ду При получении того же статуса при повторной попытке выполнения команды:
Текущая команда должна быть удалена. В журнал должно быть добавлено сообщение «СКЛАД: Не могу по- ложить подшипник в выходную ячейку после второй попытки». Если порядковый номер, результирующей ячейки любой другой кроме 999 и 0, то: В журнал должно быть добавлено сообщение «СКЛАД: Не могу по- ложить подшипник в ячейку № <Номер_ячейки>». Текущая команда должна быть удалена. На первое место в очереди должна быть добавлена команда SetN — «Произвести зануление». На второе место в очереди должна быть добавлена команда с параме- трами удаленной в п. 2 команды. При получении того же статуса при повторной попытке выполнения команды: Ячейка должна быть помечена как сбойная (не должны предприни- маться дальнейшие попытки положить в нее подшипники). В журнал должно быть добавлено сообщение «СИСТЕМА: Ячейка <Номер Стороны> <Номер_Колонки> <Номер_Ряда> помечена как сбой- ная». Текущая команда должна быть удалена. На первое место в очередь должна быть добавлена команда с параме- трами удаленной команды, но номер результирующей ячейки в команде должен быть заменен номером другой свободной ячейки, соответственно алгоритму поиска свободной ячейки.
Приложение 3. Высокоуровневый дизайн Назначение Данный документ описывает внутреннюю структуру, взаимодейст- вие с окружением и внешние интерфейсы приложения. Приводится опи- сание классов, их взаимодействие, а также описание их внешних и вну- тренних интерфейсов. Определения и принятые сокращения В настоящем документе приняты следующие определения и сокра- щения: Сокращение Определение FS Документ содержит технические требования, предъявляемые к функционированию программного продукта HLD High Level Design. Содержит описание модульной структуры проекта и взаимодействия его модулей Проект Проект симулятора морского боя Слово «должен» определяет необходимое требование к продукту. Слова «может», «предполагает», «способен» определяют направление ра- бот, которое подлежит дальнейшему уточнению. Описание структуры проекта Пользовательский уровень представления системы изображен на рис. 3.1. Детальное описание пользовательского уровня приведено в FS. Представление тестируемой системы с точки зрения окружения приведено на рис. П-3.2. Взаимодействие системы со складом и терминалами должно осуще- ствляться посредством вызова методов модулей системы. Методы внешнего модуля Axle //Получение сообщения от терминала оси static public int GetAxlePar(out string NameMaster, out string Factory, out string Number, out byte ShiftNum, out int Side, out float FrontDiam, out float BackDiam)
«subsystem» Top Package: :WareHouse Рис. П-3.2. Окружение системы Методы внешнего модуля Bearing // Получение сообщения от терминала подшипника static public int GetRollerPar(out string NameMaster, out string Factory, out string Number, out int ShiftNum, out int Group, out int SepType, out float AShift)
Методы внешнего модуля Store И Получение сообщения от СКЛАДА static public long GetStoreMessage() //Получение статуса СКЛАДА static public int GetStoreStat(out long mParametr) /I Посылка команды СКЛАДУ static public int SendStoreCom (int com, int si, int yl, int xl, int s2, int y2, int x2) ОПИСАНИЕ КЛАССОВ Класс TBearingParam // Класс параметров подшипника public class TBearingParam { public string Number; // Номер подшипника public int ShiftNum; // Номер рабочей смены public DateTime OutDateTime; // Дата и время выдачи подшипника public DateTime InDateTime; // Дата и время поступления подшипника public string Factory; // Название депо public string NameMaster; // ФИО мастера public int GroupNum; И Номер группы подшипника public int SepType; И Тип сепаратора подшипника public float AShift; public int Position; II Осевой сдвиг // Позиция на оси (0 - на заднем кольце (Back), // 1 - на переднем кольце (Front)) // Конструктор public TBearingParam() } Класс реализует набор параметров подшипника.
Класс TTerminalBearing // Класс терминала подшипника public class TTerminalBearing { private TBearingParam BearingParam;//Структура параметров I/подшипника public TCommandQueue CommandQueue; //Ссылка на очередь //команд public bool IsQuery; //Флаг, разрешающий //опрос терминала // Конструктор public TTerminalBearing() // Опрашивает терминал private long QueryTerminal() // Запрашивает и обрабатывает статус терминала public void Process() } Класс используется для взаимодействия с терминалом подшипника. Операции: • Конструктор TTerminalBearing () инициализирует поле BearingParam и устанавливает значение флага IsQuery в true. • Метод QueryTerminal () вызывает функцию внешнего модуля IBearing.GetRollerPar (...), присваивает возвращенные значения полю BearingParam и возвращает значение статуса терминала, • Метод Process () вызывает метод QueryTerminal (). Если статус терминала равен 0 (это означает, что параметры подшипника были успешно возвращены), то на первое место в очередь команд доба- вляется команда GetR — получить из входной ячейки: CommandQueue.AddCommand(TCommand.GetR,-1,0,-1, this.BearingParam, null,0); Если получены другие значения статуса терминала, то в журнал со- общений добавляется запись в соответствии с FS пункт l.a.iv.
2.3. Класс TAxleParam II Класс параметров оси public class TAxleParam { public byte ShiftNum; public string NameMaster; public string Factory; public string Number; public int Side; public float BackDiam; public float FrontDiam; } //Номер рабочей смены //ФИО мастера //Название депо //Номер оси //Сторона оси 0 - правая, //1 - левая //Номер группы задний //Номер группы задний Класс реализует набор параметров оси. Класс TTerminalAxle II Класс терминала оси public class TTerminalAxle { private TAxleParam AxleParam; //Структура параметров I/подшипника public TStore Store; //Ссылка на склад //Опрашивает терминал private long QueryTerminal() //Конструктор public TTerminalAxle() //Запрашивает и обрабатывает статус терминала public void Process() } Класс используется для взаимодействия с терминалом оси. Операции: • Конструктор TTerminalAxle () инициализирует поле AxleParam. • Метод QueryTerminal () вызывает функцию внешнего модуля lAxle.GetAxlePar (...), присваивает возвращенные значения полю AxleParam и возвращает значение статуса терминала. • Метод Process!) вызывает метод QueryTerminal (). Если статус терминала равен 0 (это означает что параметры оси были успешно
возвращены), то вызывается метод Store.FindBearinginStore подбирающий подшипники для данной оси. В зависимости от ре- зультатов подбора в журнал выводится сообщение (FS пункт 2). Если получены другие значения статуса терминала, то в журнал со- общений добавляется запись в соответствии с FS пункт 2. Класс TCommand // Класс команды public class TCommand { public const short GetR = 1; //Получить из входной public const short //ячейки SendR = 2; //Отправить из ячейки в public const short //выходную ячейку MoveR = 3; //Переложить из ячейки public const short //в ячейку PutR = 4; //Положить в резерв public const short PutRl = 5; //Положить при выдаче public const short SetN = 6; //Произвести зануление public const short CheckR = 9; //Проверить ячейку на public const short //занятость PRoll = 10; //Получить параметры public const short //подшипника Term = 20;//Завершение команд выдачи public int NameCommand; //Название команды public int NRetry; //Число попыток выполнения команды public int CellSource; //Порядковый номер исходной ячейки public int CellTarget; //Порядковый номер //результирующей ячейки public int TagSt; //Сторона результирующей ячейки public int TagCol; //Колонка результирующей ячейки public int TagRow; //Ряд результирующей ячейки public int SourseSt; //Сторона исходной ячейки public int SourseCol; //Колонка исходной ячейки public int SourseRow; //Ряд исходной ячейки public TBearingParam PR; //Структура характеристик //подшипника public TAxleParam РА;//Структура характеристик оси public string GetFullName() }
XV?. Приложение 3 Высокоуровноныи nust. Класс реализует команду складу. Команда складу описи вае i: • код; • название команды; • полное название команды. Код и название команды используются внутри системы и при взаи- модействии со складом, а полное название команды используется при до- бавлении в журнал сообщений записей о работе системы. Класс содержит описание всех допустимых команд (табл. П-3.1). Табл. П-3.1. Список команд складу № Код Название Полное название 1 1 GetR Получить из входной ячейки 2 2 SendR Отправить из ячейки в выходную ячейку 3 4 PutR Положить в резерв 4 6 SetN Произвести зануление 5 20 Term Завершение команд выдачи Операции: • Операция GetFullName () возвращает полное название команды, соответствующее коду команды (табл. П-3.1), указанному в поле NameCommand, если он является допустимым кодом. В против- ном случае возвращается сообщение «ОШИБКА: Неверный код команды». Может применяться в любоД момент. Для выполнения конструктора не требуется никаких предваритель- ных условий. Для выполнения деструктора не требуется никаких предварительных условий.
Класс ТСотт andQueue public class TCoromandQueue : System.Windows.Forms.ListBox { private TTerminalBearing TerminalBearing; //Терминал подшипника private TStore Store; //Склад // Конструктор public TcommandQueue(TStore store,// Ссылка на экземпляр //TStore TTerminalBearing terminalBearing) // Ссылка на экземпляр I/TTerminalBearing // Добавляет команду в очередь команд на указанную позицию public void AddCommand(int NameCommand, // Код команды int CntRoll, // Номер выдаваемого подшипника int CellSourse, // Порядковый номер исходной ячейки int CellTarget, // Порядковый номер результирующей // ячейки TBearingParam PR, // Параметры подшипника TAxleParam РА, // Параметры оси int Position) // Позиция в очереди // Удаляет команду из очереди public void DeleteCommand(int Position) // Выполняет первую команду в очереди public void ProcessCommandO // Отправляет команду складу private int SendCommand() } Класс реализует очередь FIFO объектов типа TCommand. Наследуется от System.Windows.Forms.ListBox библиотеки .NET. Количество команд в очереди не ограничено. Имеет ссылки на экземпляры классов TTerminalBearing и TStore. Операции: • Конструктор TcommandQueue (...) создает экземпляр класса и ини- циализирует поля TerminalBearing и Store с помощью передава- емых указателей. Передаваемые указатели должны указывать на существующие объекты, внутри конструктора такой проверки не происходит.
• Операция AddCommand (...) создает объект типа TCommand, присваи- вает ему переданные параметры и добавляет в очередь команд на указанную позицию. При добавлении команды GetR (см. табл. 3.1 «Список команд складу») должен быть запрещен опрос терминала подшипника, т.е. полю TerminalBearing.IsQuery должно быть присвоено значение false. Позиции в очереди нумеруются, начи- ная с 0. Значение позиции в очереди = -1 означает, что команда бу- дет добавлена в конец очереди. Можно также явно задавать пози- цию, на которую следует добавить команду. • Операция DeleteCommand (...) удаляет команду из очереди на ука- занной позиции. При удалении команды GetR (см. табл. 2.1 «Спи- сок команд складу») должен быть разрешен опрос терминала под- шипника, т.е. полю TerminalBearing.IsQuery должно быть при- своено значение true. • Операция Processcommand () при наличии команд в очереди посы- лает первую команду из очереди складу при помощи метода SendCommand (). В зависимости от возвращенного этим методом значения — статуса команды — должны быть предприняты следую- щие действия, описанные в FS, глава 3 «Специфические требова- ния», пункт 3. •Операция SendCommand() посылает первую в очереди команду (с индексом 0) складу с помощью функции IStore. SendStoreCom () из Store.dll и возвращает код ответа склада. Перед отправкой коман- ды устанавливаются следующие ее поля: — Для команды GetR «Получить из входной ячейки» координаты ячейки источника — (SourseSt = 0, SourseCol = 0, SourseRow = 0), а координаты результирующей ячейки возвращает метод TStore. FindFreeCell () с параметром, определяющим зону склада, для поиска установленным в true (поиск в рабочей зоне склада). — Для команды SendR «Отправить из ячейки в выходную ячейку» координаты результирующей ячейки — (TagSt = 9, TagCol = 9,TagRow = 9), а координаты ячейки источника возвращает ме- тод TStore. GetCoord () на основе значения поля CelISourse посылаемой команды. — Для команды PutR «Положить в резерв» координаты ячейки ис- точника — (SourseSt - -1, SourseCol = -1, SourseRow = -1), а координаты результирующей ячейки возвращает метод TStore.FindFreeCell() с параметром, определяющим зону склада для поиска установленным в false (поиск в резервной зоне склада). Для выполнения деструктора не требуется никаких предварительных условий.
Класс TStore public class TStore { public TTerminalBearing TerminalBearing;//Ссылка на //терминал подшипника public TCommandQueue CommandQueue;//Ссылка на очередь //команд private static string Connectionstring;//Строка //подключения к серверу БД private SqlConnection connFindFreeCell; private SqlConnection connFindBearing; private SqlConnection connAddBearing; private SqlConnection connMarkFree; private SqlConnection connRemoveBearing; private SqlConnection connGetCoord; // Возвращает статус склада private long GetStatus() // Добавляет запись об обслуженной ОСИ в базу данных private bool AddBearingAxle(TCommand Command) // Добавляет запись о подшипнике в базу данных private bool AddBearing(TCommand Command) // Удаляет запись о подшипнике из базы данных private bool RemoveBear,ing(TCoinmand Command) // Помечает ячейку как проблемную private bool MarkCellBad(int Cell) 11 Возвращает сообщение склада о выполнении команды private long GetMessage() // Конструктор public TStore() // Запрашивает и обрабатывает статус склада public void Process() 11 Запрашивает и обрабатывает сообщение склада public void ProcessMessage() // Находит свободную ячейку public bool FindFreeCell(ref int CNum, ref int TagSt, ref int TagRow, ref int TagCol, bool IsReserve) // Возвращает координаты ячейки по номеру public bool GetCoord(int CNum, ref int Side, ref int Row, ref int Col) //Находит подшипники в складе на основании параметров ОСИ public bool FindBearinglnStore(TAxleParam AxleParam) }
Класс реализует терминал склада. Операции: • Метод GetStatus () вызывает функцию внешнего модуля IStore.GetStoreStat (...) и в случае, если она вернула О, GetStatus () возвращает значение статуса склада, в противном случае возвращается -1. • Метод AddBearingAxle () добавляет запись об обслуженной оси в базу данных на основе переданной в качестве параметра команды. • Метод AddBearing () добавляет запись о принятом подшипнике в базу данных на основе переданной в качестве параметра команды. • Метод RemoveBearing () удаляет запись о выданном подшипнике из базы данных на основе переданной в качестве параметра ко- манды. • Метод MarkCelIBad (...) помечает ячейку как проблемную, поряд- ковый номер ячейки передается как параметр. • Метод GetMessage () вызывает функцию внешнего модуля IStore .GetStoreMessage (...) и возвращает код сообщения склада • Конструктор TStore () читает из конфигурационного файла имя сервера SQL, имя пользователя, пароль и инициализирует все по- ля типа SqlConnection. • Метод Process () вызывает метод GetStatus (). В зависимости от полученного статуса склада он производит следующие действия: — Статус = 32. Добавляется сообщение в журнал и вызывается ме- тод обработки ситуации прихода подшипника — TerminalBearing.Process(). — Статус = 16. Добавляется сообщение в журнал и вызывается ме- тод постановки команды «Положить в резерв» в очередь CommandQueue.AddCommand(). — Статус = 8. Добавляется сообщение в журнал. — Статус = 4. Добавляется сообщение в журнал и вызывается ме- тод постановки команды «Произвести зануление» в очередь CommandQueue.AddCommand(). — Статус = 0. Добавляется сообщение в журнал. — При любом другом статусе в журнал добавляется сообщение об ошибке. • Метод ProcessMessage () вызывает метод GetMessage () и в зависи- мости от полученного результата выполняет следующие действия: — Полученный результат —1. Добавляется сообщение в журнал. Предпринимается повторная попытка получить сообщение, после второй неудачной попытки происходит выход из прило жен ия. — Полученный результат — 0. Добавляется сообщение в журнал
Продолжается опрос (вызов метода GetMessage ()). — Полученный результат -1. Добавляется сообщение в журнал. Производит обновление базы данных по выданному или при- нятому подшипнику и удаляет команду из очереди. — Полученный результат -2. Добавляется сообщение в журнал. Пытается выполнить команду повторно, после второй неудач- ной попытки удаляет команду из очереди. — Полученный результат -3. Добавляется сообщение в журнал. Пытается выполнить команду повторно, после второй неудач- ной попытки удаляет команду из очереди. Операция FindFreeCell (...) ищет свободную ячейку в резервной (параметр IsReserve установлен в true) или рабочей (параметр IsReserve установлен в false) области с наименьшим порядковым номе- ром и возвращает ее координаты: порядковый номер — CNum, сторона — TagSt, ряд — TagRow, колонка — TagCol. Операция GetCoord(„.) возвращает для переданного порядкового номера ячейки (CNum) номер строки (Side), колонки (Col) и ряда (Row). Класс TLog public class TLog { static private FileStream fs = new FileStreamf"system.log", FileMode.Create,FileAccess.Write, FileShare.ReadWrite); static private Streamwriter srLog = new Streamwriter(fs) ; // Деструктор - TLogO // Добавляет запись в журнал сообщений системы static public void AddToLog(string LogMessage) } Класс реализует журнал сообщений системы. Операции: • Метод AddToLog () сохраняет переданное ему в качестве парамет- ра сообщение в файл.
Класс TModel public class TModel { private TLog Log; //Ссылка на журнал сообщений private TStore Store; //Ссылка на склад private TTerminalAxle TerminalAxle; //Ссылка на //терминал оси private TTerminalBearing TerminalBearing; //Ссылка на //терминал подшипника private TCommandQueue CommandQueue; //Ссылка на очередь //команд public Thread QueryThread; //Поток опроса public Thread CommandThread; //Поток команд public bool QueryThreadTerminated; //Флаг окончания //работы потока опроса public bool CommandThreadTerminated;//Флаг окончания //работы потока команд // Конструктор public TModel() // Метод, реализующий поток опроса public void QueryThreadExecute() // Метод, реализующий поток выполнения команд public void CommandThreadExecute() } Класс реализует журнал сообщений системы. Операции: • Метод QueryThreadExecute () реализует поток опроса. Здесь пос- ледовательно вызываются методы TStore. Process () и TTerminalAxle.Process(). • Метод CommandThreadExecute () реализует поток выполнения ко- манд. Здесь вызывается метод TcommandQueue. Processcommand ().
ОПИСАНИЕ ВЗАИМОСВЯЗЕЙ КЛАССОВ Взаимосвязи классов Общая диаграмма классов, используемых в системе Система должна быть реализована в виде набора классов, представ- ленного на рис. П-3.3. Рис. П-3.3. Диаграмма классов
Описание интерфейсного взаимодействия Общедоступные интерфейсы классов (подробное описание приве- денных методов изложено в п. 2): TStore // Запрашивает и обрабатывает статус склада public string Process() // Запрашивает и обрабатывает сообщение склада public string ProcessMessage() // Находит свободную ячейку public bool FindFreeCell(ref int CNum, ref int TagSt, ref int TagRow, ref int TagCol, bool IsReserve) // Возвращает координаты ячейки по номеру public bool GetCoord(int CNum, ref int Side, ref int Row, ref int Col) // Находит подшипники в складе на основании параметров ОСИ public bool FindBearinglnStore(TAxleParam AxleParam) TTerminalBearing II Конструктор public TTerminalBearing() // Запрашивает и обрабатывает статус терминала public void Process() TTerminalAxle II Конструктор public TTerminalAxle() // Запрашивает и обрабатывает статус терминала public void Process()
TCommandQueue II Конструктор public TcommandQueue(TStore store, TTerminalBearing terminalBearing) // Добавляет команду в очередь команд на указанную позицию public void AddCommand(int NameCommand, int CntRoll, int CellSourse, int CellTarget, TBearingParam PR, TAxleParam PA, int Position) // Удаляет команду из очереди public void DeleteCommand(int Position) // Выполняет первую команду в очереди public void ProcessCommandO TBearingParam II Конструктор public TBearingParam() TAxleParam II Конструктор public TAxleParam() TModel II Конструктор public TModel() // Метод, реализующий поток опроса public void QueryThreadExecute() // Метод, реализующий поток выполнения команд public void CommandThreadExecute() TCommand II Возвращает полное название команды public string GetFullName()
TLog // Добавляет запись в журнал сообщений системы static public void AddToLog(string LogMessage) TMainForm 11 Конструктор public MainForm
Литература 1. Beizer В. Software Testing Techniques. — ITP, 1990. — 550 pp. 2. Boehm B. Software Engineering Economic. — Prentice—Hall,Inc, N.J. 1981.-767 pp. 3. Макгрегор Дж, Сайкс Д . Тестирование объектно- ориентированного программного обеспечения. — К: Диасофт, 2002. - 432с. 4. Брукс Ф. Мифический человеко-месяц или как создаются программные системы. — СПб.: Символ—Плюс, 1999. — 304с. 5. Липаев В.В. Тестирование программ. — М.: Радио и связь, 1986. - 296с. 6. Канер С., Фолк Дж., Нгуен Енг. Тестирование программного обеспечения. - К: ДиаСофт, 2000 - 544с. 7. Рекомендации по преподаванию информатики в университетах - СПб., 2002 - 372с. 8. IEEE Software Engineering Standards Collection 1997 Edition 9. IEEE Std 610.12—1990, IEEE Standard Glossary of Software Engineering Technology 10. Шимаров В. А. Тестирование программ: цели и особенности инструментальной поддержки // Программное обеспечение ЭВМ / АН БССР. Институт математики. — Минск, 1994. — Вып. 100 -с. 19-43 11. Goodenough J.B., Gerhart S.L. Toward a Theory of Test Data Selection. — IEEE Transactions on Software Engineering, 1975, SE—1, №.2, p. 156-193 12. Moranda P.B. Asymptotic Limits to Program Testing, — INFOTECH State of Art deport "Software Testing", v/2, 1979, p.201—210 13. Halstead M. Elements of Software Science. — Elsevier North—Holland, Inc. 1977, pp. 109 14. Herman P. M. A Data Flow Analysis Approach to Program Testing // Australian Computer Jornal. — 1976. — Vol. 8, № 3. pp. 92 — 96 15. Ntafos S. C. A Comparition of Some Structural Testing Strategies // IEEE Transaction on Software Engineering. — 1988. — Vol. SE—14, № 6. pp. 868 — 874 16. Борзов Ю. В., Уртанс Г. Б., Шимаров В. А. Выбор путей программы для построения тестов // УСиМ. — 1989. — N. 6 — с.29 — 36 17. Shimarov V. A. Definition and quantitative estimation of testing criteria // Software Quality Concern for people. Proceedings of the Fourth European Conference on Software Quality. October 17 — 20, 1994, Basel, Switzerland, pp. 350 -360
18. McCabe Т J., Schulmeyer G. G. System Testing Aided by Structured Analysis (A Practical Experience) // COMPSAC'82. Proc. IEEE Comput. Soc. 6th International Computer Software and AppL Conference (Chicago, Ill, Nov. 8—12, 1982). — pp. 523 — 528 19. McCabe T. J., Butler Ch. W. Design complexity measurement and testing // Communications of the ACM. 32, 12 (Dec, 1989), pp. 1415 — 1425 20. Prather R., Myers J. P, Jr. The path prefix software testing strategy // IEEE Transactions on Software Engineering SE—13, 7 (July, 1987), pp. 761 — 766 21. Фролов А. В., Фролов Г. В. Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT I I M.: ДИАЛОГ-МИФИ, 1996. — 288 с. — (Библиотека системного программиста; T. 24) 22. Dorman М. N. C++ — It's Testing, Jim, But Not As We Know It I I In Proc, of 5th European Conference in Software Testing, Analysis and Review (Edinburgh, Scotland, November 1997). — 13 p.., 23. Jorgensen P. C., Erickson C. Object-Oriented Integration Testing // Communications of the ACM. 37, 9 (Sept, 1994), pp. 30 — 38 24. Майерс Г. Искусство тестирования программ // М.: Финансы и статистика, 1982. —176 с., Beizer В. Software testing techniques. (Second edit.) //International Thomson Computer Press, 1990. — 550p.
Приложение Глоссарий тестирования содержит сводку стандартных терминов, определения которых взяты из источников [8], [9]: Acceptance testing[IEEE Std 1012-1986] 1. Formal testing conducted to determine whether of not system satisfies its acceptance criteria and to enable the customer to determine whether of not to accept the system. 2. Formal testing conducted to enable a user, customer, or other author- ized entity to determine whether to accept a system or component. Breakpoint A point in a computer program at which execution can be suspended to permit manual or automated monitoring of program performance or results. Type include code breakpoint, data breakpoint, dynamic break- point, epilog breakpoint, programmable breakpoint, prolog breakpoint, static breakpoint. Note: N breakpoint is said to be set when both a point in the program and an event that will cause suspension of execution at that point are defined; it is said to be initiated when program execution is suspended. Bug See: error, fault Crash The sudden and complete failure of a computer system or component Debug To detect, locate, and correct faults in a computer program. Techniques include use of breakpoints, desk, checking, dumps, inspection, reversible execution, single-step operation, and traces. Design 1. The process of defining the architecture, components, interfaces and other characteristics of a system or component 2. The result of the process in (1) Design description A document that describes the design of a system or component. Typical components include system or component architecture, control logic, data structure, input/output formats, interface description, and algorithm Design element [IEEE Std 990-1987] A basic components or building block in a design
Design entity [IEEE Std 1016-1987] An element (component) of a design that is structurally and functionally distinct from other elements that is separately named and referenced. Design language A specification language with special constructs, and, sometimes, verifi- cation protocols, used to develop, analyze, and document a hardware or software design. Design level [IEEE Std 829-1983] The design decomposition of the software item (for example, system, sub- system, program, or module). Design phase The period time in software life cycle during which the design for archi- tecture, software components, interfaces, and data are created, docu- mented, and verified to satisfy requirements. Design requirement A requirement that specifies or constrains the design of a system or sys- tem component. Design review A process or meeting during which a system, hardware, or software design is presented to project personnel, managers, users, customers, or other interested parties for comment or approval. Design specification See: design description Design standard [IEEE Std 1002-1987] A standard that describes the characteristics of a design or a design description of data or program components Design unite [IEEE Std 990-1987] A logically related collection of design elements. Design view [IEEE Std 1016-1987] A subset of design entity attribute information that is specially suited to the needs of software project activity Detailed design 1. The process of refining and expanding the preliminary design of a sys- tem or components to the extent that the design is sufficiently complete to be implemented. 2. The result of the process in (1).
Development cycle See: software development cycle Development life cycle See: software development cycle Development specification See: requirements specification Development testing Formal or informal testing conducted during the development of a system or component, usually in the development environment by the developer Efficiency The degree to which a system or component its designated functions with minimum consumption of resources. Embedded computer system A computer system that is the part of a larger system and performs some of the requirements of that system, for example, a computer system used in a car. Embedded software Software that is part of a larger system and performs some of the require- ments of that system, for example, software used in a car computer sys- tem. Emulation 1. A model that accepts the same inputs and produces the same outputs as a given system. 2. The process of developing or using a model as in (1). Entity In computer programming, any item that can be named or denoted in a program. For example, a data item, program statement, or subprogram. Entry point A point of a software program at which execution of the module can begin. Epilog breakpoint A breakpoint that is initiated upon exit from a given program or routine Error 1. The difference between a computed, observed, or measured value or condition and the true, specified, or theoretically corrected value or con- dition. For example, a difference of 3 second between a computed result and the corrected result.
2. An incorrect step, process, or data definition. For example, an incor- rect instruction in a computer program.3. An incorrect result. For exam- ple, a computed result of when corrected result is 10.4. A human action that produced an incorrect result. For example, an incorrect action on the part of a programmer or operator. Note: While all four definitions are commonly used, one distinction assigns definitions 1 to the word "error", definitions 2 to the word "fault", defini- tions 3 to the word "failure", and definitions 4 to the word "mistake". Error model In software evaluation, a model used to estimate or predict the number of remaining faults, required test time, and similar characteristics of a system. Error prediction A quantitative statement about expected number or nature of faults in a system or component. Error tolerance The ability of a system or component to continue normal operation despite the presence of erroneous inputs. Exception An event that causes suspension of normal program execution. Execute To carry out an instruction, process, or computer program. Execution efficiency The degree to which a system or component performs its designated func- tions with minimum consumption of time. Execution time The amount of elapsed time or processor time used in executing a com- puter program. Execution trace A record of sequence of instructions executed during the execution of a computer program. Often takes the form of a list of code labels encoun- tered as the program executes. Failure The inability of a system or component to perform its required functions within specified performance requirements. Note: The fault tolerance discipline distinguishes between a human action (a mistake), its manifestation (a hardware or software fault), the result of the fault (a failure), and amount by which the result is incorrect.
Fatal error An error that results in the complete inability of a system or component to function. Fault 1. A defect in hardware device or component. 2. An incorrect step, process, or data definition in a computer program. Note: This definition is used primarily by the fault tolerance discipline. In common usage the terms "error” and "bug" are used to express meaning. Integration The process of combining software components, hardware components, or both into an overall system. Integration testing Testing in which software components, hardware components, or both are combined and tested to evaluate the interaction between them. Interface 1. A shared boundary across which information is passed. 2. A hardware or software component that connects two or more other components for the purpose of passing information from one to other. 3. To connect two or more components for the purpose of passing infor- mation from one to other.5. To serve as a connecting or connected component as in (2) Interface requirement A requirement that specifies as external item with which a system or sys- tem component must interact, or that sets forth constrains on formats, timing, or other factors caused by such as interaction. Interface specification A document that specifies the interface characteristics of an existing or planned system or component Interface testing Testing conducted to evaluate whether systems or components pass data and control correctly to one another Invariant An assertion that should always be true for specified segment or at a spec- ified point of a computer program
Mutation testing A testing methodology in which two or more program mutations air rxr cuted using the same test cases to evaluate the ability of test cases I о detect differences in the mutations Path 1. In software engineering, a sequence of instructions that may be per- formed in the execution of a computer program. 2. In file access, a hierarchical sequence of directory and subdirectory names specifying the storage location of a file. Part analysis Analysis of a computer program to identify all possible paths through the program, to detect incomplete paths, or to discover portions of the pro- gram that are not on any path. Path conditions A set of conditions that must be met in order for a particular program path to be execute. Path expression A logical expression indicating the input conditions that must be met in order for a particular program path to be execute. Path testing Testing designed to execute all or selected pass through a computer program. Performance requirement A requirement that imposes conditions on a functional requirement; for example, a requirement that specified the speed, accuracy, or memory usage with which a given function must be performed. Performance specification A document that specifies the performance characteristics that a system or component must passes. These characteristics typically include speed, accuracy, and memory usage. Often part of a requirement specification. Performance testing Testing conducted to evaluate the compliance of a system or component with specified performance requirements. Postmortem dump A dump that is produced upon abnormal termination of a computer program.
Preliminary design 1. The process of analyzing design alternatives and defining the architec- ture, components, interfaces, and timing and sizing estimations for a sys- tem or component, 2. The result of process in (1) Product specification 1. A document that specifies the design that production copies of a system or component must implement. For software, this document describes the as-build version of software. 2. A document that describes the characteristics of a planned or existing product for consideration by potential customers or users. Product standard[IEEE Std 1002-1987] A standard that defines what constitutes completeness and acceptability of item that are used or produced, formally or informally, during the soft- ware engineering process. Programmable breakpoint A breakpoint that automatically invokes a previously specified debugging process when initiated. Prolog breakpoint A breakpoint that is initiated upon entry into a program or routine. Protocol A set of conventions that govern the interaction of processes, devices, and other components within a system. Qualification testing Testing conducted to determine whether a system or component suitable for operational use. Regression testing Selective retesting of system or component to verify that modifications have not caused unintended effect and that the system or component still complies with its special requirements. Requirement I. A condition or capability needed by a user to solve a problem or achieve an objective. 2. A condition or capability that must be met or processed by a system or system component to satisfy a contract, standard, specification, or other formally imposed documents.
3. A documented representation of condition or capability as in (1) or (2). Requirement analysis 1. The process of studying user needs to arrive at a definition of system, hardware or software requirements. 2. The process of studying and refining system, hardware or software requirements. Requirements phase The period time in the software life cycle during which the requirements for a software product are defined and documented. Requirements review A process or meeting during which the requirements for a system, hard- ware item, or software item are presented to project personnel, managers, users, customers, or other interested parties for comment or approval. Types include system requirements review, software requirements review, Requirements specification A document that specified requirements for a system or component. Typically included are functional requirements, performance require- ments, interface requirements, design requirements, and development standards. Requirements specification language A specification language with special constructs and, sometimes, verifica- tion protocols, used to develop, analyze, and document hardware or soft- ware requirements. Selective dump A dump of designed storage location areas only. Selective trace A variable trace that involves only selected variables. Semanic error An error resulting from a misunderstanding of the relationship of symbol or groups of symbols to their meanings in a given language. Soft failure A failure that permits continued operation of a system with partial capa- bility.
Software design description (SDD) 1. [IEEE Std 1012-1986] A representation of software created to facilitate analysis, planning, implementation, and decision making. The software design description is used as a medium for communicating software design information, and may be thought of as blueprint or model of the system. 2. [IEEE Std 1016-1987] A representation of a software system created to facilitate analysis, planning, implementation, and decision making. A blueprint or model of the software system. The SDD is used as the pri- mary medium for communicating software design information. Software development cycle The period of time that begins with the decision to develop a software product and ends when the software is delivered. This cycle typically includes a requirements phase, design phase, implementation phase, test phase, and sometimes, installation and checkout phase. Notes: (1) The phases listed above may overlap or be performed iteratively, depending upon the software development approach used. (2) This term is sometimes used to mean a longer period of time, either the period that ends when software is no longer being enhanced by the developer, or the entire software life cycle. Software development process The process by which user needs are translated into software product. The process involves translating user needs into software requirements, trans- forming the software requirements into design, implementing the design in code, testing the code, and sometimes, installing and checking out the software for operational use.Note: These activities may overlap or be per- formed iteratively. Software life cycle The period of time that begins when the software is conceived and ends when software is no longer available to use. This software life cycle typi- cally includes a concept phase, requirements phase, design phase, imple- mentation phase, test phase, installation and checkout phase, operation and maintenance phase, and sometimes, retirement phase. Notes: (1) The phases may overlap or be performed iteratively. Software requirements specification (SRS) [IEEE Std 1012-1986] Documentation of the essential requirements (functions, performance, design, constrains, and attributes) of software and its external interface.
Software test incident [IEEE Std 1008-1987] Any event occurring during the execution of a software test that requires investigation. Specification A document that specifies, in a complete, precise, variable manner, the requirements, design, behavior, or other characteristics of a system or component, and, often the procedures for determining whether these provisions have been satisfied. Statement testing Testing designed to execute each statement of computer program. Stress testing Testing conducted to evaluate a system or component at or beyond the limits of its specified requirements. Structural testing Testing that takes into account the internal mechanism of a system or component. Types include branch testing, path testing, statement testing. Stub (1) A skeleton of special-purpose implementation of a software module, used to develop or test a module that calls or is otherwise dependent on it. (2) A computer program statement substituting for the body of a software module that is or will be defined elsewhere. System testing Testing conducted on a complete, integrated system to evaluate the sys- tem’s compliance with its specified requirements. Test 1. An activity in which a system or component is executed under specified conditions, the results are observed or recorded, and an evaluation is made of some aspect of the system or component. 2. To conduct an activity in (1). 3. [IEEE Std 829-1983] A set of one or more test cases. 4. [IEEE Std 829-1983] A set of one or more test procedures. 5. [IEEE Std 829-1983] A set of one or more test cases and procedures. Test bed An environment containing the hardware, instrumentation, simulators, software tools, and other support elements needed to conduct a test.
Test case 1. A set of test inputs, execution conditions, and expected results devel- oped for a particular objective, such as to exercise a particular program path or to verify compliance with a specific requirement. 2. [IEEE Std 829-1983] Documentation specifying inputs, predicate results, and a set of execution condition for a test item. Tease case generator A software tool that accepts as input source code, test criteria, specifica- tions, or data structure definitions; uses these inputs to generate test input data, and sometimes, determines expect results. Test case specification A document that specifies the test inputs, execution conditions, and pred- icated results for an item to be tested. Test coverage The degree to which a given test or set of tests addressed all specified requirements for a given system or component. Test criteria The criteria that a system or component must meet in order to pass a given test. Test design [IEEE Std 829-1983] Documentation specifying the details of the test approach for a software features and identifying the associated tests. Test documentation Documentation describing plans for, or results of, the testing of a system or component. Types include test case specification, test incident report, test log, plan, test procedure, test report. Test driver A software module used to invoke a module under test and, often, provide test inputs, control and monitor execution, and report test results. Test incident report A document that describe an event that occurred during testing which requires further investigation. Test item [IEEE Std 829-1983] A software item which is an object of testing. Test item transmittal report[IEEE Std 829-1983] A document that identifies one or more items submitted for testing. It contains current status and location information.
Test log A chronological record of нН relevant details about the execution ol a lest. Test objective[IEEE Std 1008-1987) An identified set of software features to be measured under specified con ditions by comparing described in the software documentation. Test phase The period of time in the software life cycle during which the components of a software product are evaluated and integrated, and the software prod- uct is evaluated to determine whether or not requirements have been sat- isfied, Test plan 1. [IEEE Std 829-1983] A document describing the scope, approach, resources, and schedule of intended test activities. It identifies test items, the features to be tested, the testing tasks, who will do each task, and any risks requiring contingency planning. 2. A document that describes the technical and management approach to be followed for testing a system or component. Typical contents identify the items to be tested, tasks to be performed, responsibilities, schedules, and required resources for the testing activities. Test procedure 1. Detailed instructions for the set-up, execution, and evaluation of results for a given test case. 2, A document containing a set of associated instructions as in (1). 3. [IEEE Std 829-1983] Documentation specifying a sequence of actions for the execution of a test. Test procedure specification See: test procedure Test readiness review 1. A review conducted to evaluate preliminary test results for one or more configuration items; to verify that the test procedures for each configura- tion item are complete, comply with test plans and descriptions, and sat- isfy test requirements; and to verify that a project is prepared to proceed to formal testing of the configuration items. 2. A review as in (1) for any hardware or software component. Test repeatability An attribute of a test, indicating that the same results are produced each time the test is conducted.
Test report A document that describes the conduct and results of the testing carried out for a system or component. Test script See: test procedure Test set architecture [IEEE Std 1008-1987] The nested relationship between sets of test cases that directly reflect the hierarchic decomposition of the test objectives. Test specification See: test case specification Test summary report [IEEE Std 1008-1987] A document summarizing testing activities and results. It also contains an evaluation of corresponding test items. Test unite [IEEE Std 1008-1987] A set of one or more computer program modules together with associated control data (for example, tables), usage procedures, and operating pro- cedures that satisfy the following conditions: (a) All modules are from a single computer program; (b) At least one of the new or changed modules in the set has not completed the unit test; (c) The set of modules together with associated data and procedures are the sole object of a testing process. Testability 1. The degree to which a system or component facilitates the establish- ment of test criteria and performance of tests to determine whether those criteria have been met. 2. The degree to which a requirement is stated in terms that permit estab- lishment of test criteria and performance of tests to determine whether those criteria have been met. Testing 1. The process of operating a system or component under specified con- ditions, observing or recording the results, and making an evaluation of some aspect of the system or component. 2. [IEEE Std 829-1983] The process of analyzing a software item to detect the differences between existing and required conditions (that is, bugs) and to evaluate features of software items.
Trace 1. A record of the execution of a computer program, showing the sequence of instructions executed, names and values of variables, or both. Types include execution trace, retrospective trace, subroutines trace, sym- bolic trace, variable trace. 2. To produce a record as in (1). 3. To establish a relationship between two or more products of the devel- opment process; for example, to establish a relationship between a given requirement and the design element that implements that requirement. Traceability 1. The degree to which a relationship can be established between two or more products of the development process, especially products having a predecessor-successor or master-subordinate relationship to one another; for example, the degree to which the requirements and design of a given software component match. 2. The degree to which each element in a software development product establishes its reason for existing; for example, the degree to which each element in a bubble chart references the requirements that it satisfy. Traceability matrix A matrix that records the relationship between two or more products of the development process; for example, a matrix that records relationship between requirements and the design of a given software component. Unit testing Testing of individual hardware or software units or groups of related units. / Validation The process of evaluating a system or component during or at the end of the development process to determine whether is satisfies specified requirements. Verification 1. The process of evaluating a system or component to determine whether the product of a given development phase satisfy the conditions imposed at the start of that phase. 2. Formal proof of program correctness. Verification and validation (V&V) The process of determining whether requirements for a system or compo- nent are complete and correct, the product of each development phase fulfill the requirements or conditions imposed by the previous phase, and the final system or component complies with specified requirements.
ОСНОВЫ ТЕСТИРОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ УЧЕБНОЕ ПОСОБИЕ Предлагаемый вниманию читателей курс является разделом двухсемест- рового курса «Технология индустри- ального программирования», читае- мого в Санкт-Петербургском госу- дарственном политехническом уни- верситете на факультете технической кибернетики. Курс обобщает много- летний опыт работы учебного центра «Политехник Моторола» в области подготовки специалистов промыш- ленного программирования и напра- влен на подготовку тестировщиков программного проекта. Этот курс следует рассматривать как дополни- тельный учебник по тестированию, поскольку объем лекционного мате- риала и практикумов существующих вузовских программ для подготовки тестировщиков программных проек- тов явно недостаточен. Курс и сопро- вождающий его практикум по тести- рованию рассчитаны на студентов программистских специальностей, желающих получить знания и навыки, необходимые для работы в области промышленного тестирования каче- ственных программных продуктов. Предлагаемый материал может окв- заться полезным и для преподавате- лей вузов в качестве методического руководства. Всеволод Котляров Татьяна Коликоаа ISBX 5-94774-406-4