10. Тестирование объектно-ориентированных программных систем
10.1 Особенности тестирования объектно-ориентированных программных системТестирование представляет собой процесс поиска ошибок путем серии контрольных запусков программы с заранее подготовленным набором входных данных и сравнения результатов выполнения со спецификациями на программу. Тестирования как известно не гарантирует правильность программы, но позволяет получить хотя бы какую-то оценку этой правильности. Разработаны различные методы тестирования, и некоторые из них, например, метод тестирования всех ветвей, обеспечивают высокую надежность при обнаружении ошибок.
Методы тестирования структурных программ достаточно давно и хорошо проработаны ( тем не менее даже для структурных программ тестирование не является полностью закрытой темой ). Объектно-ориентированные программы неизбежно добавляют специфические проблемы к технологии тестирования. Эти проблемы определяются двумя основными аспектами [11]:
- граф управления программы уже не является полностью определяющим элементом этой программы, в связи с тем, что классы должны составляться с учетом свойств предметной области и возможностей решения других задач.
- элементом тестирования является не подпрограмма, структура которой хорошо представляется и по интерфейсу и по графу управления, а класс, имеющий набор атрибутов и методов поведения, которые не связанны друг с другом жестко.
Объектно-ориентированный подход не гарантирует создания правильных программ. Следовательно тестирования так же необходимо для объектно-ориентированных программ как и для структурных. Преимущества этого подхода, выражающиеся в возможности повторного использования, приводят к необходимости более тщательного тестирования. Классы обычно создаются заранее с возможностью использования в различных задачах, а следовательно одна задача не может полностью обеспечить тестирования взаимодействия методов поведения классов.
Основные свойства объектов ( инкапсуляция, наследование, полиморфизм ) добавляют новые аспекты тестирования.
Инкапсуляция
создает проблему видимости данных, так как они доступны только через операции. В процессе тестирования это может создать определенные проблемы с выводом значений.Наследование
ставит вопросы о повторении тестирования наследуемых операций. Допустим операция А принадлежит базовому классу, и она протестирована. Операция В принадлежит производному классу и вызывает операцию А. Должна ли операция А тестироваться повторно?Полиморфизм
приводит к неоднозначности с вызовом операций, которая может быть разрешена только на этапе выполнения. Следовательно заранее построить и спланировать набор тестов становится просто невозможным.Объектно-ориентированная технология привносит свои особенности в процесс тестирования систем. Формулируется несколько вопросов, которые необходимо разрешить для успешного проведения тестирования:
- Какая часть унаследованных свойств должна заново тестироваться.
- Когда и как можно проверять информациию о состоянии класса.
- Как можно проверить поведение системы, зависящее от состояния, когда отсутствует единый механизм управление состояниями в программе.
- Как следует тестировать интеграцию классов и какие стратегии тестирования применять.
Решение подобных вопросов выполняется путем разработки новых подходов и модернизации старых, специально для тестирования объектно-ориентированных систем.
10.2 Методы тестирования объектно-ориентированных систем
Выбор базового компонента для тестирования. Основной единицей тестирования должен являться класс (объект). Отдельные методы класса бесполезно рассматривать в отрыве от самого класса, а прочие компоненты являются обычно агрегацией классов. Предназначение класса в системе (например, абстрактный класс) определяет особенности его тестирования.
Класс представляет собой набор атрибутов и методов ( обычно часть из них скрыта ) и, следовательно, граф управления не применим. Отметим основные черты нового модуля тестирования:
- нет глобальных данных ( или они сведены к минимуму в виде констант ),
- класс не является тестируемым элементом, тестироваться могут только объекты, т.е. экземпляры класса,
- нельзя тестировать методы поведения класса изолированно друг от друга, поскольку они используют общие атрибуты, значения которых могут меняться в зависимости от набора и последовательности вызываемых методов.
Тестирование экземпляра класса (объекта) может проводится изолированно, однако класс можно считать полностью протестированным, только после того как его экземпляр будет создан и проверен в рамках системы.
Тестирование наследования.
Наследуемые методы должны быть протестированы заново при переопределении или дополнении базового метода. Это связано с тем, что новый контекст атрибутов, новые аспекты поведения могут добавить новые ошибки. Таким образом если класс А является базовым для класса В. То сначала тестируются все методы класса А, затем все методы класса В причем так, что все методы класса А, вызываемые из В тестируются заново.Инкапсуляция
, сама по себе, не является источником ошибок, однако представляет препятствие для проведения тестирования, так как при тестировании требуется получение полной информации о состоянии класса, в том числе и всех его атрибутов, многие из которых могут быть объявлены как "невидимые" извне. Решение этой проблемы может быть найдено в определении специальных отладочных методов, возвращающих информацию о состоянии класса или в использовании низкоуровневых отладчиков программного кода.Тестирование полиморфизма
Каждый возможный вариант полиморфного вызова метода должен быть протестирован. Примером такого случая является класс А, содержащий метод МА, вызывающий в свою очередь полиморфный метод МАV. Тогда для любого производного класса В, переопределяющего метод MAV, должен быть выполнен тест по вызову метода МА.Тестирование с учетом внутренней структуры
Такой метод обычно именуют "white-box testing" (метод "прозрачного ящика"). Тестирование выполняется путем рассмотрения структуры и определения возможных тестовых последовательностей. Существует два варианта подобного тестирования: тестирование, основанное на интерфейсе класса и тестирование, основанное на методах класса. В первом случае набор тестов составляется так, чтобы проверить функционирование интерфейсных свойств объекта, таких как сообщения и исключения.Основными методами составления тестов является анализ графа передачи управления в рамках методов объекта, и анализ потока данных.
Тестирование без учета внутренней структуры
Так называемое тестирование методом "черного ящика" ("Black-box testing"). Набор тестов составляется опираясь на спецификацию и требования к классу. Структура класса при этом не анализируется.Надо учитывать, что любая спецификация не отражает (и не должна отражать) всех деталей реализации, поэтому анализ программного кода все же потребуется. Поэтому, различия между "white-box" и "
black-box" тестированием уменьшаются.Тестирование, основанное на состояниях объекта
При тестировании на основе состояний, набор тестов определяется на основе моделирования класса как конечного автомата. Результаты выполнения методов касса рассматриваются как переходы между состояниями класса. Автоматная модель класса определяет последовательность смены состояний, которую и следует проверить в ходе тестирования.Основой для составления автоматной модели класса могут служить диаграммы использования, также допустимо определение допустимых состояний методом
"white box" (т.е. на основе анализа программного кода).Подобное тестирование не лишено сложностей. Класс может быть ориентирован на любую последовательность вызова методов и в этом случае тестирование состояний будет неэффективно. Управление сменой состояний объекта может быть рассредоточено по всему приложению. Такое "совместное" управление делает изолированное тестирование классов очень сложным. Выходом может является составление глобальной автоматной модели приложения, с целью определения в ней взаимодействия классов.
Проблемы адекватности и охвата при тестировании.
Тестирование каждого класса, с учетом дерева иерархии, может выполнятся многократно. Это обеспечивает общее соответствие требованиям по тестированию, однако не гарантирует охвата всех возможных причин появления ошибок.Кроме тестирования всех классов, следует выполнять проверки операций, реализуемых в системе, так как их выполнение может быть связанно с многократным вызовом методов самых разных классов.
Рекомендуется проводить тестирование, основанное на возможных ошибках. Источниками информации в этом случае могут являться спецификации программного средства ( проект, пользовательская документация и т.п. ) или исходный текст программы.
Интеграция классов
для создания прикладной системы тесно переплетается с общим подходом к разработке. Существуют две стратегии интеграции: базирующаяся на задаче и базирующаяся на использовании. Первая определяет всю цепочку классов, начиная с главного (управляющего) класса и заканчивая классами "исполнителями". Если каждый класс проверен, то вся задача считается протестированной. Интеграция, основанная на использовании, применяется когда существует несколько или вообще не существует управляющих классов. Отдельные классы используют другие группы классов для выполнения своих задач. Тестирование такого вида интеграции осуществляется путем выделения отдельных групп классов, и тестирование каждого класса, использующего данную группу.Объектно-ориентированный подход отличается тем, что с переходом на более высокий уровень иерархии классов, объемы работ по созданию класса и, соответственно, возможности тестирования уменьшаются. Проблемой в этом случае является определения того, когда следует тестировать унаследованные свойства класса, интеграцию классов. В каждом конкретном случае следует составлять план тестирования.