Оценка тестового покрытия на проекте. Test policy — управление тестовой моделью

07.08.2019

Традиционный подход к автоматическим тестам выглядит примерно так - тестописатель изучает тестируемую систему и после этого руками пишет каждый отдельный сценарий для проверки искомой системы. Кто-то может написать тут гордое слово "handcrafted", а я называю это словом "handjob". А все потому, что обычно этот подход к созданию и написанию тестов страдает от двух проблем:

  • "Парадокс пестицида", описанный Борисом Бейзером в 1990-м году. Заключается он в том, что тесты все менее и менее эффективны в отлове багов, так как баги, для обнаружения которых эти тесты написаны, уже найдены и починены. Если же этого не происходит, то возникают серьезные вопросы к написанному коду и к рабочим процессам
  • Тесты статичны и их сложно менять, в то время как тестируемая система имеет свойство постоянно эволюционировать, обрастать новым функционалом и менять поведение старого. И тесты нужно менять каждый раз, когда функционал изменяет внешний вид программы или ее поведение. И с ростом сложности обновления тестов оправдывать чудовищные издержки на поддержку тестов становиться все сложнее.

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

Что такое модель?

Модель - это описание тестируемой системы. Формальная спецификация вполне сойдет. Модель должна быть сильно проще описываемой системы и как-то помогать нам понимать и предсказывать поведение тестируемого продукта.

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

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

Что такое Model-Based Testing?

Это довольно немолодая идея использовать формально описанные модели для того, чтобы сделать тестирование ПО более дешевым и простым занятием. Само Model-Based Testing это такая "продвинутая" техника тестирования через "черный ящик". У нее есть ряд бонусов перед традиционными методами:

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

И самое важное - формально описанные модели в комбинации с зачатками теории графов помогает легко и непринужденно генерировать сотни тестов.

Зоркий поклонник Agile может воскликнуть "эй! у нас есть BDD и оно покрывает первые три пункта и еще это спецификация!". Я же отвечу "нихрена подобного - ваши примеры станут нормальной спецификацией только тогда, когда короля Шака Зулу можно будет считать спецификацией на все человечество".

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

Короткий ликбез по теории графов

Теория графов зародилась в 1736-м году в стареньком Прусском городе Кёнингсберге. Город стоял на двух берегах реки и попутно занимал еще и пару островов посреди этой самой реки. Жители этого города от безделья пытались придумать как посетить все семь мостов не проходя ни по одному дважды. Решали на практике, во время прогулок, и в теории, во время кухонных посиделок. Долгое время никто не мог доказать или опровергнуть возможность существования данного маршрута, пока не пришел зануда Эйлер и не испортил горожанам праздник.

Эйлер придумал изобразить каждый кусок суши как вершину графа, а мосты - ребрами графа.

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

С тех пор граф, в котором все вершины имеют четное количество ребер называется "Эйлеровым Графом". А полный обход этого графа носит гордое имя "Эйлерова пути".

И после этого жителям Кёнингсберга пришлось найти себе другое развлечение. Только один китайский математик Мэй-Ку Куан все морочил себе голову этими мостами. А беспокоил его следующий вопрос:

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

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

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

И в честь Куана эту задачку назвали "задачей китайского почтальона".

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

Тут мы введем еще один термин - орграф. Или ориентированный граф. Это такой граф, ребра которого можно пересекать только в указанном направлении. Направленные же ребра так же называются "дугами".

И если в случае Эйлерова Пути или Проблемы Китайского Почтальона мы оперировали дугами касающимися вершин, то тут приходится принимать во внимание еще и направление движения. И доля "Эйлеризации" такого графа нам требуется чтобы количество входящих в вершину дуг равнялось количеству исходящих. И считая каждую входящую дугу как "+1", а исходящую как "-1" мы можем вычислять "полярность" каждой вершины орграфа. Например вершина в двумя входящими и одной исходящей дугой имеет полярность "2 - 1 = 1".

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

Причем тут тестирование?

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

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

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

Такой подход как раз активно использовался для тестирования конечных автоматов. К тому же это требование естественно вытекает из комбинаторной техники дизайна тестов под названием "все пары".

Решение предложил некий де Брюийн. Алгоритм выглядит примерно так:

  • Рисуем сбоку граф, где каждое ребро исходного графа является вершиной.
  • Там где у исходного графа дуга "1" входит в вершину, откуда выходит дуга "2" рисуем в свежеиспеченном графе дугу из вершины "1" в вершину "2".
  • Эйлеризуем полученный граф.
  • Рисуем Эйлеров путь на данном графе.

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

Про то, зачем сюда добавляют Цепи Маркова, и как обычно решается распараллеливание таких тестов я напишу позже. А пока подведем краткие итоги.

Итого

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

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

И, поскольку Теория Графов позволяет нам работать непосредственно с моделью:

  • Новые обходы можно автоматически генерировать при изменении модели
  • Наши тесты могут легко и непринужденно меняться в рамках одной и той же модели
  • Различные алгоритмы обхода могут удовлетворять различным потребностям тестирования
  • Полученные алгоритмы обхода легко можно переиспользовать в совершенно новой среде
  • Tutorial

Доброго времени суток!

Хочу собрать всю самую необходимую теорию по тестирвоанию, которую спрашивают на собеседованиях у trainee, junior и немножко middle. Собственно, я собрал уже не мало. Цель сего поста в том, чтобы сообща добавить упущенное и исправить/перефразировать/добавить/сделатьЧтоТоЕщё с тем, что уже есть, чтобы стало хорошо и можно было взять всё это и повторить перед очередным собеседованием про всяк случай. Вообщем, коллеги, прошу под кат, кому почерпнуть что-то новое, кому систематизировать старое, а кому внести свою лепту.

В итоге должна получиться исчерпывающая шпаргалка, которую нужно перечитать по дороге на собеседование.

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

В теме: определение тестирования, качество, верификация / валидация, цели, этапы, тест план, пункты тест плана, тест дизайн, техники тест дизайна, traceability matrix, tets case, чек-лист, дефект, error/deffect/failure, баг репорт, severity vs priority, уровни тестирования, виды / типы, подходы к интеграционному тестированию, принципы тестирования, статическое и динамическое тестирование, исследовательское / ad-hoc тестирование, требования, жизненный цикл бага, стадии разработки ПО, decision table, qa/qc/test engineer, диаграмма связей.

Поехали!

Тестирование программного обеспечения - проверка соответствия между реальным и ожидаемым поведением программы, осуществляемая на конечном наборе тестов, выбранном определенным образом. В более широком смысле, тестирование - это одна из техник контроля качества, включающая в себя активности по планированию работ (Test Management), проектированию тестов (Test Design), выполнению тестирования (Test Execution) и анализу полученных результатов (Test Analysis).

Качество программного обеспечения (Software Quality) - это совокупность характеристик программного обеспечения, относящихся к его способности удовлетворять установленные и предполагаемые потребности.

Верификация (verification) - это процесс оценки системы или её компонентов с целью определения удовлетворяют ли результаты текущего этапа разработки условиям, сформированным в начале этого этапа. Т.е. выполняются ли наши цели, сроки, задачи по разработке проекта, определенные в начале текущей фазы.
Валидация (validation) - это определение соответствия разрабатываемого ПО ожиданиям и потребностям пользователя, требованиям к системе .
Также можно встретить иную интерпритацию:
Процесс оценки соответствия продукта явным требованиям (спецификациям) и есть верификация (verification), в то же время оценка соответствия продукта ожиданиям и требованиям пользователей - есть валидация (validation). Также часто можно встретить следующее определение этих понятий:
Validation - ’is this the right specification?’.
Verification - ’is the system correct to specification?’.

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

Этапы тестирования:
1. Анализ
2. Разработка стратегии тестирования
и планирование процедур контроля качества
3. Работа с требованиями
4. Создание тестовой документации
5. Тестирование прототипа
6. Основное тестирование
7. Стабилизация
8. Эксплуатация

Тест план (Test Plan) - это документ, описывающий весь объем работ по тестированию, начиная с описания объекта, стратегии, расписания, критериев начала и окончания тестирования, до необходимого в процессе работы оборудования, специальных знаний, а также оценки рисков с вариантами их разрешения.
Отвечает на вопросы:
Что надо тестировать?
Что будете тестировать?
Как будете тестировать?
Когда будете тестировать?
Критерии начала тестирования.
Критерии окончания тестирования.

Основные пункты тест плана
В стандарте IEEE 829 перечислены пункты, из которых должен (пусть - может) состоять тест-план:
a) Test plan identifier;
b) Introduction;
c) Test items;
d) Features to be tested;
e) Features not to be tested;
f) Approach;
g) Item pass/fail criteria;
h) Suspension criteria and resumption requirements;
i) Test deliverables;
j) Testing tasks;
k) Environmental needs;
l) Responsibilities;
m) StafÞng and training needs;
n) Schedule;
o) Risks and contingencies;
p) Approvals.

Тест дизайн - это этап процесса тестирования ПО, на котором проектируются и создаются тестовые случаи (тест кейсы), в соответствии с определёнными ранее критериями качества и целями тестирования.
Роли, ответственные за тест дизайн:
Тест аналитик - определяет «ЧТО тестировать?»
Тест дизайнер - определяет «КАК тестировать?»

Техники тест дизайна

Эквивалентное Разделение (Equivalence Partitioning - EP) . Как пример, у вас есть диапазон допустимых значений от 1 до 10, вы должны выбрать одно верное значение внутри интервала, скажем, 5, и одно неверное значение вне интервала - 0.

Анализ Граничных Значений (Boundary Value Analysis - BVA) . Если взять пример выше, в качестве значений для позитивного тестирования выберем минимальную и максимальную границы (1 и 10), и значения больше и меньше границ (0 и 11). Анализ Граничный значений может быть применен к полям, записям, файлам, или к любого рода сущностям имеющим ограничения.

Причина / Следствие (Cause/Effect - CE) . Это, как правило, ввод комбинаций условий (причин), для получения ответа от системы (Следствие). Например, вы проверяете возможность добавлять клиента, используя определенную экранную форму. Для этого вам необходимо будет ввести несколько полей, таких как «Имя», «Адрес», «Номер Телефона» а затем, нажать кнопку «Добавить» - эта «Причина». После нажатия кнопки «Добавить», система добавляет клиента в базу данных и показывает его номер на экране - это «Следствие».

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

Traceability matrix - Матрица соответствия требований - это двумерная таблица, содержащая соответсвие функциональных требований (functional requirements) продукта и подготовленных тестовых сценариев (test cases). В заголовках колонок таблицы расположены требования, а в заголовках строк - тестовые сценарии. На пересечении - отметка, означающая, что требование текущей колонки покрыто тестовым сценарием текущей строки.
Матрица соответсвия требований используется QA-инженерами для валидации покрытия продукта тестами. МСТ является неотъемлемой частью тест-плана.

Тестовый случай (Test Case) - это артефакт, описывающий совокупность шагов, конкретных условий и параметров, необходимых для проверки реализации тестируемой функции или её части.
Пример:
Action Expected Result Test Result
(passed/failed/blocked)
Open page «login» Login page is opened Passed

Каждый тест кейс должен иметь 3 части:
PreConditions Список действий, которые приводят систему к состоянию пригодному для проведения основной проверки. Либо список условий, выполнение которых говорит о том, что система находится в пригодном для проведения основного теста состояния.
Test Case Description Список действий, переводящих систему из одного состояния в другое, для получения результата, на основании которого можно сделать вывод о удовлетворении реализации, поставленным требованиям
PostConditions Список действий, переводящих систему в первоначальное состояние (состояние до проведения теста - initial state)
Виды Тестовых Случаев:
Тест кейсы разделяются по ожидаемому результату на позитивные и негативные:
Позитивный тест кейс использует только корректные данные и проверяет, что приложение правильно выполнило вызываемую функцию.
Негативный тест кейс оперирует как корректными так и некорректными данными (минимум 1 некорректный параметр) и ставит целью проверку исключительных ситуаций (срабатывание валидаторов), а также проверяет, что вызываемая приложением функция не выполняется при срабатывании валидатора.

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

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

Error - ошибка пользователя, то есть он пытается использовать программу иным способом.
Пример - вводит буквы в поля, где требуется вводить цифры (возраст, количество товара и т.п.).
В качественной программе предусмотрены такие ситуации и выдаются сообщение об ошибке (error message), с красным крестиком которые.
Bug (defect) - ошибка программиста (или дизайнера или ещё кого, кто принимает участие в разработке), то есть когда в программе, что-то идёт не так как планировалось и программа выходит из-под контроля. Например, когда никак не контроллируется ввод пользователя, в результате неверные данные вызывают краши или иные «радости» в работе программы. Либо внутри программа построена так, что изначально не соответствует тому, что от неё ожидается.
Failure - сбой (причём не обязательно аппаратный) в работе компонента, всей программы или системы. То есть, существуют такие дефекты, которые приводят к сбоям (A defect caused the failure) и существуют такие, которые не приводят. UI-дефекты например. Но аппаратный сбой, никак не связанный с software, тоже является failure.

Баг Репорт (Bug Report) - это документ, описывающий ситуацию или последовательность действий приведшую к некорректной работе объекта тестирования, с указанием причин и ожидаемого результата.
Шапка
Короткое описание (Summary) Короткое описание проблемы, явно указывающее на причину и тип ошибочной ситуации.
Проект (Project) Название тестируемого проекта
Компонент приложения (Component) Название части или функции тестируемого продукта
Номер версии (Version) Версия на которой была найдена ошибка
Серьезность (Severity) Наиболее распространена пятиуровневая система градации серьезности дефекта:
S1 Блокирующий (Blocker)
S2 Критический (Critical)
S3 Значительный (Major)
S4 Незначительный (Minor)
S5 Тривиальный (Trivial)
Приоритет (Priority) Приоритет дефекта:
P1 Высокий (High)
P2 Средний (Medium)
P3 Низкий (Low)
Статус (Status) Статус бага. Зависит от используемой процедуры и жизненного цикла бага (bug workflow and life cycle)

Автор (Author) Создатель баг репорта
Назначен на (Assigned To) Имя сотрудника, назначенного на решение проблемы
Окружение
ОС / Сервис Пак и т.д. / Браузера + версия /… Информация об окружении, на котором был найден баг: операционная система, сервис пак, для WEB тестирования - имя и версия браузера и т.д.

Описание
Шаги воспроизведения (Steps to Reproduce) Шаги, по которым можно легко воспроизвести ситуацию, приведшую к ошибке.
Фактический Результат (Result) Результат, полученный после прохождения шагов к воспроизведению
Ожидаемый результат (Expected Result) Ожидаемый правильный результат
Дополнения
Прикрепленный файл (Attachment) Файл с логами, скриншот или любой другой документ, который может помочь прояснить причину ошибки или указать на способ решения проблемы.

Severity vs Priority
Серьезность (Severity) - это атрибут, характеризующий влияние дефекта на работоспособность приложения.
Приоритет (Priority) - это атрибут, указывающий на очередность выполнения задачи или устранения дефекта. Можно сказать, что это инструмент менеджера по планированию работ. Чем выше приоритет, тем быстрее нужно исправить дефект.
Severity выставляется тестировщиком
Priority - менеджером, тимлидом или заказчиком

Градация Серьезности дефекта (Severity)

S1 Блокирующая (Blocker)
Блокирующая ошибка, приводящая приложение в нерабочее состояние, в результате которого дальнейшая работа с тестируемой системой или ее ключевыми функциями становится невозможна. Решение проблемы необходимо для дальнейшего функционирования системы.

S2 Критическая (Critical)
Критическая ошибка, неправильно работающая ключевая бизнес логика, дыра в системе безопасности, проблема, приведшая к временному падению сервера или приводящая в нерабочее состояние некоторую часть системы, без возможности решения проблемы, используя другие входные точки. Решение проблемы необходимо для дальнейшей работы с ключевыми функциями тестируемой системой.

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

S4 Незначительная (Minor)
Незначительная ошибка, не нарушающая бизнес логику тестируемой части приложения, очевидная проблема пользовательского интерфейса.

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

Градация Приоритета дефекта (Priority)
P1 Высокий (High)
Ошибка должна быть исправлена как можно быстрее, т.к. ее наличие является критической для проекта.
P2 Средний (Medium)
Ошибка должна быть исправлена, ее наличие не является критичной, но требует обязательного решения.
P3 Низкий (Low)
Ошибка должна быть исправлена, ее наличие не является критичной, и не требует срочного решения.

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

1. Модульное тестирование (Unit Testing)
Компонентное (модульное) тестирование проверяет функциональность и ищет дефекты в частях приложения, которые доступны и могут быть протестированы по-отдельности (модули программ, объекты, классы, функции и т.д.).

2. Интеграционное тестирование (Integration Testing)
Проверяется взаимодействие между компонентами системы после проведения компонентного тестирования.

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

4. Операционное тестирование (Release Testing).
Даже если система удовлетворяет всем требованиям, важно убедиться в том, что она удовлетворяет нуждам пользователя и выполняет свою роль в среде своей эксплуатации, как это было определено в бизнес моделе системы. Следует учесть, что и бизнес модель может содержать ошибки. Поэтому так важно провести операционное тестирование как финальный шаг валидации. Кроме этого, тестирование в среде эксплуатации позволяет выявить и нефункциональные проблемы, такие как: конфликт с другими системами, смежными в области бизнеса или в программных и электронных окружениях; недостаточная производительность системы в среде эксплуатации и др. Очевидно, что нахождение подобных вещей на стадии внедрения - критичная и дорогостоящая проблема. Поэтому так важно проведение не только верификации, но и валидации, с самых ранних этапов разработки ПО.

5. Приемочное тестирование (Acceptance Testing)
Формальный процесс тестирования, который проверяет соответствие системы требованиям и проводится с целью:
определения удовлетворяет ли система приемочным критериям;
вынесения решения заказчиком или другим уполномоченным лицом принимается приложение или нет.

Виды / типы тестирования

Функциональные виды тестирования
Функциональное тестирование (Functional testing)
Тестирование безопасности (Security and Access Control Testing)
Тестирование взаимодействия (Interoperability Testing)

Нефункциональные виды тестирования
Все виды тестирования производительности:
o нагрузочное тестирование (Performance and Load Testing)
o стрессовое тестирование (Stress Testing)
o тестирование стабильности или надежности (Stability / Reliability Testing)
o объемное тестирование (Volume Testing)
Тестирование установки (Installation testing)
Тестирование удобства пользования (Usability Testing)
Тестирование на отказ и восстановление (Failover and Recovery Testing)
Конфигурационное тестирование (Configuration Testing)

Связанные с изменениями виды тестирования
Дымовое тестирование (Smoke Testing)
Регрессионное тестирование (Regression Testing)
Повторное тестирование (Re-testing)
Тестирование сборки (Build Verification Test)
Санитарное тестирование или проверка согласованности/исправности (Sanity Testing)

Функциональное тестирование рассматривает заранее указанное поведение и основывается на анализе спецификаций функциональности компонента или системы в целом.

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

Тестирование взаимодействия (Interoperability Testing) - это функциональное тестирование, проверяющее способность приложения взаимодействовать с одним и более компонентами или системами и включающее в себя тестирование совместимости (compatibility testing) и интеграционное тестирование

Нагрузочное тестирование - это автоматизированное тестирование, имитирующее работу определенного количества бизнес пользователей на каком-либо общем (разделяемом ими) ресурсе.

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

Объемное тестирование (Volume Testing) . Задачей объемного тестирования является получение оценки производительности при увеличении объемов данных в базе данных приложения

Тестирование стабильности или надежности (Stability / Reliability Testing) . Задачей тестирования стабильности (надежности) является проверка работоспособности приложения при длительном (многочасовом) тестировании со средним уровнем нагрузки.

Тестирование установки направленно на проверку успешной инсталляции и настройки, а также обновления или удаления программного обеспечения.

Тестирование удобства пользования - это метод тестирования, направленный на установление степени удобства использования, обучаемости, понятности и привлекательности для пользователей разрабатываемого продукта в контексте заданных условий. Сюда также входит:
Тестирование пользовательского интерфейса (англ. UI Testing) - это вид тестирования исследования, выполняемого с целью определения, удобен ли некоторый искусственный объект (такой как веб-страница, пользовательский интерфейс или устройство) для его предполагаемого применения.
User eXperience (UX) - ощущение, испытываемое пользователем во время использования цифрового продукта, в то время как User interface - это инструмент, позволяющий осуществлять интеракцию «пользователь - веб-ресурс».

Тестирование на отказ и восстановление (Failover and Recovery Testing) проверяет тестируемый продукт с точки зрения способности противостоять и успешно восстанавливаться после возможных сбоев, возникших в связи с ошибками программного обеспечения, отказами оборудования или проблемами связи (например, отказ сети). Целью данного вида тестирования является проверка систем восстановления (или дублирующих основной функционал систем), которые, в случае возникновения сбоев, обеспечат сохранность и целостность данных тестируемого продукта.

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

Дымовое (Smoke) тестирование рассматривается как короткий цикл тестов, выполняемый для подтверждения того, что после сборки кода (нового или исправленного) устанавливаемое приложение, стартует и выполняет основные функции.

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

Повторное тестирование - тестирование, во время которого исполняются тестовые сценарии, выявившие ошибки во время последнего запуска, для подтверждения успешности исправления этих ошибок.
В чем разница между regression testing и re-testing?
Re-testing - проверяется исправление багов
Regression testing - проверяется то, что исправление багов не повлияло на другие модули ПО и не вызвало новых багов.

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

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

Предугадывание ошибки (Error Guessing - EG) . Это когда тест аналитик использует свои знания системы и способность к интерпретации спецификации на предмет того, чтобы «предугадать» при каких входных условиях система может выдать ошибку. Например, спецификация говорит: «пользователь должен ввести код». Тест аналитик, будет думать: «Что, если я не введу код?», «Что, если я введу неправильный код? », и так далее. Это и есть предугадывание ошибки.

Подходы к интеграционному тестированию:

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

Сверху вниз (Top Down Integration)
Вначале тестируются все высокоуровневые модули, и постепенно один за другим добавляются низкоуровневые. Все модули более низкого уровня симулируются заглушками с аналогичной функциональностью, затем по мере готовности они заменяются реальными активными компонентами. Таким образом мы проводим тестирование сверху вниз.

Большой взрыв («Big Bang» Integration)
Все или практически все разработанные модули собираются вместе в виде законченной системы или ее основной части, и затем проводится интеграционное тестирование. Такой подход очень хорош для сохранения времени. Однако если тест кейсы и их результаты записаны не верно, то сам процесс интеграции сильно осложнится, что станет преградой для команды тестирования при достижении основной цели интеграционного тестирования.

Принципы тестирования

Принцип 1 - Тестирование демонстрирует наличие дефектов (Testing shows presence of defects)
Тестирование может показать, что дефекты присутствуют, но не может доказать, что их нет. Тестирование снижает вероятность наличия дефектов, находящихся в программном обеспечении, но, даже если дефекты не были обнаружены, это не доказывает его корректности.

Принцип 2 - Исчерпывающее тестирование недостижимо (Exhaustive testing is impossible)
Полное тестирование с использованием всех комбинаций вводов и предусловий физически невыполнимо, за исключением тривиальных случаев. Вместо исчерпывающего тестирования должны использоваться анализ рисков и расстановка приоритетов, чтобы более точно сфокусировать усилия по тестированию.

Принцип 3 - Раннее тестирование (Early testing)
Чтобы найти дефекты как можно раньше, активности по тестированию должны быть начаты как можно раньше в жизненном цикле разработки программного обеспечения или системы, и должны быть сфокусированы на определенных целях.

Принцип 4 - Скопление дефектов (Defects clustering)
Усилия тестирования должны быть сосредоточены пропорционально ожидаемой, а позже реальной плотности дефектов по модулям. Как правило, большая часть дефектов, обнаруженных при тестировании или повлекших за собой основное количество сбоев системы, содержится в небольшом количестве модулей.

Принцип 5 - Парадокс пестицида (Pesticide paradox)
Если одни и те же тесты будут прогоняться много раз, в конечном счете этот набор тестовых сценариев больше не будет находить новых дефектов. Чтобы преодолеть этот «парадокс пестицида», тестовые сценарии должны регулярно рецензироваться и корректироваться, новые тесты должны быть разносторонними, чтобы охватить все компоненты программного обеспечения, или системы, и найти как можно больше дефектов.

Принцип 6 - Тестирование зависит от контекста (Testing is concept depending)
Тестирование выполняется по-разному в зависимости от контекста. Например, программное обеспечение, в котором критически важна безопасность, тестируется иначе, чем сайт электронной коммерции.

Принцип 7 - Заблуждение об отсутствии ошибок (Absence-of-errors fallacy)
Обнаружение и исправление дефектов не помогут, если созданная система не подходит пользователю и не удовлетворяет его ожиданиям и потребностям.

Cтатическое и динамическое тестирование
Статическое тестирование отличается от динамического тем, что производится без запуска программного кода продукта. Тестирование осуществляется путем анализа программного кода (code review) или скомпилированного кода. Анализ может производиться как вручную, так и с помощью специальных инструментальных средств. Целью анализа является раннее выявление ошибок и потенциальных проблем в продукте. Также к статическому тестирвоанию относится тестирования спецификации и прочей документации.

Исследовательское / ad-hoc тестирование
Простейшее определение исследовательского тестирования - это разработка и выполнения тестов в одно и то же время. Что является противоположностью сценарного подхода (с его предопределенными процедурами тестирования, неважно ручными или автоматизированными). Исследовательские тесты, в отличие от сценарных тестов, не определены заранее и не выполняются в точном соответствии с планом.

Разница между ad hoc и exploratory testing в том, что теоретически, ad hoc может провести кто угодно, а для проведения exploratory необходимо мастерство и владение определенными техниками. Обратите внимание, что определенные техники это не только техники тестирования.

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

Требования к требованиям:
Корректность
Недвусмысленность
Полнота набора требований
Непротиворечивость набора требований
Проверяемость (тестопригодность)
Трассируемость
Понимаемость

Жизненный цикл бага

Стадии разработки ПО - это этапы, которые проходят команды разработчиков ПО, прежде чем программа станет доступной для широко круга пользователей. Разработка ПО начинается с первоначального этапа разработки (стадия «пре-альфа») и продолжается стадиями, на которых продукт дорабатывается и модернизируется. Финальным этапом этого процесса становится выпуск на рынок окончательной версии программного обеспечения («общедоступного релиза»).

Программный продукт проходит следующие стадии:
анализ требований к проекту;
проектирование;
реализация;
тестирование продукта;
внедрение и поддержка.

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

Жизненный цикл разработки ПО:
Пре-альфа
Альфа
Бета
Релиз-кандидат
Релиз
Пост-релиз

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

QA/QC/Test Engineer


Таким образом, мы можем построить модель иерархии процессов обеспечения качества: Тестирование - часть QC. QC - часть QA.

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

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

Цель

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

Проверку взаимодействий компонентов

Проверку правильности интеграции компонентов

Проверку точности реализации всех требований

Выявление дефектов и принятие мер, необходимых для их устранения до
развертывания программного обеспечения

Качество

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

Глава 12. Г67

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

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

Кто отвечает за качество продукта

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

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

Тестирование в итеративном жизненном цикле

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

Классификация тестов

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

Тестируемый параметр качества - какой параметр качества проходит испытания

Этап тестирования- момент жизненного цикла, в котором выполняется
тестирование

Тип теста - конкретная задача отдельного теста, как правило, связанная с одним
параметром качества

Параметры качества

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

Надежность

Программное обеспечение "сопротивляется" появлению ошибок в процессе выполнения: отсутствуют аварийные отказы, зависания, утечка памяти и т. п.

Функциональные возможности

Программное обеспечение реализует требуемые прецеденты или имеет ожидаемое поведение.

Я Производительность

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

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

Этапы тестирования

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

Блочное тестирование

Тестируются минимальные элементы системы. Время тестирования, как правило, совпадает со временем реализации элементов.

Интегральное тестирование

Тестируются интегральные блоки (или компоненты, или подсистемы).

Системное тестирование

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

Приемочное тестирование

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

Следует помнить, что в разное время жизненного цикла этапы тестирования проходят с различными акцентами. Ранний концептуальный прототип, используемый в фазе исследования для оценки жизнеспособности видения продукта, будет подвергаться различным приемочным испытаниям. Архитектурный прототип, разрабатываемый в фазе уточнения плана, будет подвержен интегральным и системным испытаниям, направленным на проверку архитектурной целостности и производительности ключевых архитектурных элементов, несмотря на то что в это время большая часть кода системы имеет форму программ-суррогатов. Этапы тестирования - это не предопределенные "фазы", последовательно выполняемые ближе к концу проекта; наоборот, при итеративном жизненном цикле тестирование начинается рано и выполняется часто.

Типы тестов

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

Аттестационный тест

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

Конфигурационный тест

Проверяет приемлемость функционирования целевого объекта тестирования при различных конфигурациях (программных или аппаратных).

Функциональные испытания

Проверяется функционирование целевого объекта тестирования в общем, т.е. должная реализация требуемых прецедентов.

Установочные испытания

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

Тестирование целостности

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

Испытание под нагрузкой

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

Эксплуатационные испытания

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

Испытания в жестком режиме

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

Регрессивное тестирование

Регрессивное тестирование - это методика испытаний, при которой тесты, производимые ранее, повторно выполняются на новой версии целевого объекта. Цель такого типа тестирования - обеспечить, чтобы качество целевого объекта не ухудшалось (не регрессировало) при добавлении к этому объекту новых функций. Регрессивное тестирование необходимо для

Максимально раннего выявления дефектов;

Проверки того, что изменения кода не приводят к новым дефектам или не
восстанавливают старые.

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

Модель тестирования

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

Рассмотрим подробнее составляющие модели тестирования.

Контрольные задачи

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

Методики испытания

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

Сценарии испытаний

Инструкции, автоматизирующие реализацию части или всей методики испытания (или методик испытания).

Классы и компоненты испытаний

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

Взаимодействия тестов

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

Примечания

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

Основные элементы модели тестирования и их взаимоотношения показаны на рис. 12.1.

Рис. 12.1. Контрольные задачи, методики испытаний и сценарии испытаний для банкомата

Исполнители и артефакты

В технологическом процессе тестирования задействованы два основных исполнителя.

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

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

Если для поддержки тестирования необходим специфический код (например, должны разрабатываться драйверы или программы-суррогаты), то в процессе должны участвовать еще разработчик и конструктор, исполняющие роли, подобные определенным в главах 10 и 11.

Исполнители и артефакты технологического процесса тестирования представлены на рис. 12.2. Давайте рассмотрим ключевые артефакты этого процесса.

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

Модель тестирования описывалась ранее.

Результаты тестирования и данные, собранные в процессе выполнения тестов.

Модель рабочей нагрузки для эксплуатационных испытаний; она определяет
переменные и их значения, используемые в различных эксплуатационных
испытаниях для моделирования или имитации характеристик внешних
исполнителей, функций, выполняемых конечными пользователями, объема
этих функций и нагрузки, создаваемой этими функциями.

Дефекты, полученные в результате "проваленных тестов" являются одним из
типов запросов на внесение изменений (см. главу 13).

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

Пакеты и классы тестов

Подсистемы и компоненты тестов

Заключительная оценка тестов используется как часть оценки итерации проекта и периодической оценки состояния (см. главу 7, "Технологический процесс управления проектом").

Технологический процесс

Типичный технологический процесс тестирования, его основные элементы и зависимости между ними показаны на рис. 12.3.

Подготовка к тестированию

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

Разработка теста

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

Реализация теста

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

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

Выполнение тестов на этапе интегрального тестирования

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

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

Выполнение тестов на этапе системного тестирования

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

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

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

Оценка тестирования

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

Инструментальная поддержка

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

Корпорация Rational Software предлагает следующие инструментальные средства, поддерживающие автоматизацию тестов и процесс тестирования в целом.

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

Robot поддерживает выполнение тестов, позволяя испытателям создавать и воспроизводить сценарии тестирования с графическим интерфейсом пользователя и сравнивать полученные и ожидаемые результаты.

LogViewer фиксирует результаты тестирования и представляет отчет для оценки выполнения теста.

TestManager поддерживает планирование, проектирование и оценку тестов, позволяет определить тестовое покрытие и генерирует отчеты о состоянии тестов.

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

PerformanceStudio выполняет сценарии тестирования виртуального пользова
теля, используя для этого эксплуатационные испытания и некоторые функци
ональные испытания.

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

Rational Purify для локализации труднообнаруживаемых ошибок времени выполнения.

Rational PureCoverage* для определения участков кода, не прошедших тестирование, и выполнения анализа покрытия кода.

Rational Quantify* для выявления фрагментов кода, ограничивающих производительность.

Кроме того, для большинства из названных средств Rational Unified Process предлагает инструментальные наставники.

Резюме

Тестирование позволяет оценить качество производимого продукта.

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

За качество отвечают все. Качество не может вноситься тестирующей органи
зацией. Тестирование направлено только на оценку качества и организацию
своевременной обратной связи, позволяющей повысить качество системы.

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

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

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

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

Управление тестовой моделью - непрерывный процесс на протяжении всего жизненного цикла продукта.

Покрытие тестовой модели

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

Качество сценариев

Для управления качеством сценариев необходимо контролировать не только уровень описания тест-кейсов, но и их качество.

До начала описания тест-кейсов необходимо определить требования для каждого уровня описания и критерии качества описания тест-кейсов.

Возможные уровни описания тест-кейсов:

На 4-м уровне согласование с заказчиком может быть заменено на согласование .

Критерии качества описания тест-кейсов могут быть следующими:

  • Тест-кейсы необходимо писать по требованиям

Тестирование - это процесс проверки соответствия продукта предъявляемым к нему требованиям. Поэтому в части общего описания тест-кейса (в тест-трекинговых системах обычно употребляется термин «Summary») необходимо ссылаться на конкретное требование в связке с фрагментами текста требований. Таким образом, для всех участников проекта будет понятно, на основании чего написан данный тест-кейс.

  • Используйте детальные предусловия

Как сэкономить время на выполнении тест-кейсов?

Установите правила форматирования для всех тест-кейсов. Так тест-кейс будет удобен для понимания и чтения для любого участника проекта. Например, на проекте можно ввести следующие правила:

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

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

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

Процесс контроля качества сценариев можно вести с помощью Test Model Control – специально заготовленного шаблона.

Актуализация тестовой модели

Необходимо регулярно проводить актуализацию тестовой модели и самих тест-кейсов на соответствие требованиям, а также пересматривать приоритеты тест-кейсов.

Для актуализации можно вести «Матрицу требований» (Requirement Traceability Matrix): после каждого изменения в определенном требовании из тест-трекинговой системы делается выборка всех связанных с этим требованием тестовых сценариев, и проводится их обновление.

Средства управления тестовой моделью:

  • TestRail
  • TestLink
  • Jira+Zephyr
  • Microsoft Test Manager (MTM)
  • Excel

18.09.2003 Александр Петренко, Елена Бритвина, Сергей Грошев, Александр Монахов, Ольга Петренко

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

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

Подходы к улучшению качества программ

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

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

В простейшем варианте набор этапов жизненного цикла таков:

  • анализ требований;
  • проектирование (предварительное и детальное);
  • кодирование и отладка ("программирование");
  • тестирование;
  • эксплуатация и сопровождение.

Стандартизованная схема жизненного цикла с четкой регламентацией необходимых работ и с перечнем соответствующей документации легла в основу так называемой «водопадной» или каскадной модели. Водопадная модель подразумевает жесткое разбиение процесса разработки программного обеспечения на этапы, причем переход с одного этапа на другой осуществляется только после того, как будут полностью завершены работы на предыдущем этапе. Каждый этап завершается выпуском полного комплекта документации, достаточной для того, чтобы разработка могла быть продолжена другой командой. Водопадная модель стала доминирующей в стандартах процессов разработки Министерства обороны США. Многие волей или неволей, даже отклоняясь от этой модели, в целом соглашались с ее разумностью и полезностью.

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

В конце 80-х годов была предложена так называемая спиральная модель, был развит и проверен на практике метод итеративной и инкрементальной разработки (Iterative and Incremental Development, IID). В спиральной модели были учтены проблемы водопадной модели. Главный упор в спиральной модели делается на итеративности процесса. Описаны опыты использования IID с длиной итерации всего в полдня. Каждая итерация завершается выдачей новой версии программного обеспечения. На каждой версии уточняются (и, возможно, меняются) требования к целевой системе и принимаются меры к тому, чтобы удовлетворить и новые требования. В целом Rational Unified Process (RUP) также следует этой модели.

Позволило ли это решить проблему качества? Лишь в некоторой степени.

Проблема повышения качества программного обеспечения в целом и повышения качества тестирования привлекает все большее внимание; в университетах вводят специальные дисциплины по тестированию и обеспечению качества, готовят узких специалистов по тестированию и инженеров по обеспечению качества. Однако по-прежнему ошибки обходятся только в США от 20 до 60 млрд. долл. ежегодно. При этом примерно 60% убытков ложится на плечи конечных пользователей. Складывается ситуация, при которой потребители вынуждены покупать заведомо бракованный товар.

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

Каково же направление главного удара? Что предлагают «наилучшие практики»?

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

В последние годы в связи с появлением методов, которые принято обозначать эпитетом agile («шустрый», «проворный») предлагаются и внедряются новые конструктивные методы раннего обнаружения ошибок. Скажем, современные модели, такие как Microsoft Solutions Framework (MSF) и eXtreme Programming (XP), выделяют следующие рекомендации к разработке тестов:

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

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

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

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

Итак, различные работы в процессе производства программ должны быть хорошо интегрированы с работами по тестированию. Соответственно, инструменты тестирования должны быть хорошо интегрированы со многими другими инструментами разработки. Из крупных производителей инструментов разработки программ, первыми это поняли компании Telelogic (набор инструментов для проектирования, моделирования, реализации и тестирования телекоммуникационного ПО, базирующийся на нотациях SDL/MSC/TTCN) и Rational Software (аналогичный набор, преимущественно базирующийся на нотации UML). Следующий шаг сделала компания IBM, начав интеграцию возможностей инструментов от Rational в среду разработки программ Eclipse.

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

Три составляющие тестирования - экскурс в теорию

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

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

Распространение компонентных технологий породило термин «компонентное тестирование» как частный случай интеграционного тестирования.

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

Инструменты тестирования - реальная практика

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

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

Обзор инструментов тестирования будем вести в обратном порядке - от системного тестирования к модульному.

Широко распространены инструменты тестирования приложений с графическим пользовательским интерфейсом. Их часто называют инструментами функционального тестирования . Если уровень ответственности приложения не велик, то таким тестированием можно ограничиться; подобное тестирование наиболее дешево.

В данном виде тестирования широко применяются инструменты записи-воспроизведения (record/playback); из наиболее известных продуктов можно назвать Rational Robot (компания IBM/Rational), WinRunner (Mercury Interactive), QARun (Compuware). Наряду с этим существуют инструменты для текстовых терминальных интерфейсов, например, QAHiperstation компании Compuware.

Для системного нагрузочного тестирования Web-приложений и других распределенных систем широко используется инструментарий LoadRunner от Mercury Interactive; он не нацелен на генерацию изощренных сценариев тестирования, зато дает богатый материал для анализа производительности, поиска узких мест, сказывающихся на производительности распределенной системы.

Примерная общая схема использования инструментов записи-воспроизведения такова:

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

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

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

Впрочем, возможности данного вида тестирования ограничены:

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

Следующий класс инструментов - инструменты тестирования компонентов . Примером является Test Architect (IBM/Rational). Такие инструменты помогают организовать тестирование приложений, построенных по одной из компонентных технологий (например, EJB). Предусматривается набор шаблонов для создания различных компонентов тестовой программы, в частности, тестов для модулей, сценариев, заглушек.

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

Последний из рассматриваемых здесь классов инструментов - инструменты тестирования модулей . Примером может служить Test RealTime (IBM/Rational), предназначенный для тестирования модулей на C++. Важной составляющей этого инструмента является механизм проверочных «утверждений» (assertion). При помощи утверждений можно сформулировать требования к входным и выходным данным функций/методов классов в форме логических условий, в аналогичной форме можно задавать инвариантные требования к данным объектов. Это существенный шаг вперед по сравнению с Test Architect. Аппарат утверждений позволяет систематическим образом представлять функциональные требования и на базе этих требований строить критерии тестового покрытия (правда, Test RealTime автоматизированной поддержки анализа покрытия не предоставляет).

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

Решение перечисленных проблем предлагает новое поколение инструментов, которые следуют подходу тестирования на основе модели (model based testing) или на основе спецификаций (specification based testing).

Чем могут помочь модели

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

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

Новый всплеск интереса к формальным методам произошел в первой половине 90-х. Его вызвали первые результаты, полученные при использовании формальных моделей и формальных спецификаций в тестировании.

Преимущества тестирования на основе моделей виделись в том, что:

  • тесты на основе спецификации функциональных требований более эффективны, так как они в большей степени нацелены на проверку функциональности, чем тесты, построенные только на знании реализации;
  • на основе формальных спецификаций можно создавать самопроверяющие (self-checking) тесты, так как из формальных спецификаций часто можно извлечь критерии проверки результатов целевой системы.

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

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

Имеется несколько ставших уже классическими нотаций формальных спецификаций: VDM, Z, B, CCS, LOTOS и др. Некоторые из них, например, VDM, используются преимущественно для быстрого прототипирования. Язык B удобен для анализа, в частности для аналитической верификации моделей. Все эти языки активно используются в рамках университетских программ. В реальной практике для описания архитектурных моделей используется UML, а для построения поведенческих моделей - языки SDL/MSC, исполнимые диаграммы UML и близкие к ним нотации.

Перечисленные языки и нотации для поведенческих моделей, к сожалению, не обладают достаточной общностью. Они хорошо себя зарекомендовали в телекоммуникационных приложениях и практически бесполезны для описания функциональности программных систем «общего вида»: операционных систем, компиляторов, СУБД и т.д.

На роль инструментов разработки тестов для подобных систем претендует новое поколение средств описания моделей/спецификаций и средства генерации тестов на проверку согласованности поведения реализации заданной модели.

Инструменты тестирования на основе моделей

Test Real Time - один из первых представителей этой группы. Более широкие возможности предоставляет Jtest компании Parasoft. Интересен инструментарий компании Comformiq. Семейство инструментов разработки тестов на основе моделей предлагает Институт системного программирования РАН в кооперации с компанией ATS. Поскольку семейство UniTesK авторам знакомо существенно ближе, мы изложим общую схему подхода тестирования на основе моделей на примерах из UniTesK.

Рис. 1. Фазы процесса разработки спецификаций и тестов

Общая схема процесса разработки спецификаций и тестов состоит из четырех фаз (рис. 1).

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

Задача второй фазы - описание требований к поведению системы. Многие подходы (например, SDL) предлагают описывать исполнимые модели, которые можно рассматривать как прототипы будущей реализации. Задание требований в таком случае определяется формулой «реализация должна вести себя так же, как модель». Подход понятен, но, к сожалению, во многих реальных ситуациях он не работает. Допустим, в заголовке некоего сообщения, построенного моделью, указано одно время, а в аналогичном заголовке от реализации - несколько другое. Это ошибка или нет? Еще один пример. Модель системы управления памятью сгенерировала указатель на свободный участок памяти, а реальная система выдала другой указатель: модель и система работают в разных адресных пространствах. Ошибка ли это?

UniTesK - унифицированное решение

UniTesK предлагает использовать так называемые неявные спецификации или спецификации ограничений. Они задаются в виде пред- и постусловий процедур и инвариантных ограничений на типы данных. Этот механизм не позволяет описывать в модели алгоритмы вычисления ожидаемых значений функций, а только их свойства. Скажем, в случае системы управления памятью модель будет задана булевским выражением в постусловии типа «значение указателя принадлежит области свободной памяти». Простой пример постусловия для функции «корень квадратный» приведен на ; одна и та же спецификация представлена в трех разных нотациях: в стиле языков Cи, Java и C#. Использование спецификационных расширений обычных языков программирования вместо классических языков формальных спецификаций - шаг, на который идут почти все разработчики подобных инструментов. Их различает только выразительная мощность нотаций и возможности анализа и трансляции спецификаций.

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

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

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

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

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

Рис. 4. Архитектура тестовой программ

Создатели UniTesK, полагая, что не должно быть отдельной среды для разработки тестов, не только наделили его возможностью мимикрии под различные языки программирования, но обеспечили интеграцию составляющих его инструментов в популярные средства разработки программ. На рис. 5 представлен сеанс использования UniTesK в среде разработки Forte 4.0 компании Sun Microsystems.

Новое качество, которое обещают новые инструменты

Как отмечалось выше, создатели инструментов тестирования обычно сталкиваются со следующими проблемами:

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

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

Критерии тестового покрытия. Основной критерий - проверка всех утверждений, в частности, утверждений, определяющих постусловия процедур или методов. Он легко проверяется и легко связывается с функциональными требованиями к целевой системе. Так, инструменты UniTesK, инструменты для платформ Java и C# предоставляют четыре уровня вложенных критериев.

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

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

Рассмотренные инструменты опробованы на реальных, масштабных проектах. Конечно, каждый проект несет в себе некоторую специфику, возможно, препятствующую исчерпывающему тестированию. Однако опыт использования данных инструментов показывает, что обычно удается достичь хороших результатов, лучших, чем результаты, полученные в аналогичных проектах при помощи ручного тестирования. Пользователи UniTesK, обычно, за приемлемый уровень качества принимают 70-80% покрытия кода целевой системы; при этом должен быть удовлетворен, как минимум, критерий покрытия всех логических ветвей в постусловиях. Для некоторых сложных программ (в том числе, для блока оптимизации компилятора GCC) был достигнут уровень покрытия 90-95%.

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

Обозначения элементов общей структуры спецификации метода:

S - Сигнатура операции

A - Спецификация доступа

- Предусловие

B - Определение ветвей функциональности

> - Постусловие

Java:
Class SqrtSpecification { S Specification static double sqrt(double x) A reads x, epsilon { = 0; } post { > if(x == 0) { B branch «Zero argument»; > return sqrt == 0; > } else { B branch «Positive argument»; > return sqrt >= 0 && > Math.abs((sqrt*sqrt-x)/x) } } } }
Си:
S specification double SQRT(double x) A reads (double)x, epsilon { = 0.; } coverage ZP { if(x == 0) { B return(ZERO, «Zero argument»); } else { B return(POS, «Positive argument»); } } post { > if(coverage(ZP, ZERO)) { > return SQRT == 0.; > } else { > return SQRT >= 0. && > abs((SQRT*SQRT - x)/x) } } }
C#:
namespace Examples { specification class SqrtSpecification { S specification static double Sqrt(double x) A reads x, epsilon { = 0; } post { > if(x == 0) { B branch ZERO («Zero argument»); > return $this.Result == 0; > } else { B branch POS («Positive argument»); > return $this.Result >= 0 && > Math.Abs(($this.Result * $this.Result - x)/x) } > } > } } }