Оглавление

1. Модели жизненного цикла программных средств. 2

2. Основные процессы жизненного цикла программного средства. 4

3. Вспомогательные процессы жизненного цикла программного средства. 11

4. Качество программного обеспечения. 16

5. Методы обеспечения надежности программных средств. 19

6. Тестирование программных средств. Комплексное тестирование. 28

7. Тестирование модулей. 35

 


1. Модели жизненного цикла программных средств

 

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

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

  каскадная модель (70-80-е годы 20 века);

  спиральная модель (80-90-е годы 20 века).

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

Рис. 2.19. Каскадная схема разработки программного средства

Положительные стороны применения каскадного подхода зак­лючаются в следующем:

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

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

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

Рис. 2.20. Схема реального процесса разработки ПС по каскадной схеме

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

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

Рис. 2.21. Схема спиральной модели жизненного цикла

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

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

 

 

2. Основные процессы жизненного цикла программного средства

Процеcc приобретения (acquisition process) состоит из действий

и задач заказчика, приобретающего программное средство (рис. 2.2).

Инициирование приобретения включает следующие задачи:

·        определение заказчиком своих потребностей в приобретении, разработке или усовершенствовании системы, программных продуктов или услуг;

·         анализ требований к системе;

·        принятие решения относительно приобретения, разработки или усовершенствования существующего ПС;

·        проверку наличия необходимой документации, гарантии, сер­тификатов, лицензий и поддержки в случае приобретения про­граммного продукта;

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

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

технические ограничения (например, среда функционирования системы).

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

Подготовка и корректировка договора включают следующие задачи:

  определение заказчиком процедуры выбора поставщика, вклю­чающей критерии оценки предложений возможных постав­щиков;

  ныбор конкретного поставщика на основе анализа предложений,

  подготовку и заключение договора с поставщиком;

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

 

Рис. 2.2. Схема процесса приобретения программного средства

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

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

Процесс поставки (supply process) охватывает действия и за­дачи, выполняемые поставщиком, который снабжает заказчика программным продуктом или услугой (рис. 2.3).

Рис. 2.3. Схема процесса поставки

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

 

Рис. 2.4. Схема процесса разработки

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

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

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

Анализ требований к ПС предполагает определение следую­щих характеристик для каждого компонента ПС:

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

  внешних интерфейсов;

  спецификаций надежности и безопасности;

   эргономических требований;

   требований к используемым данным;

   требований к установке и приемке;

   требований к пользовательской документации;

   требований к эксплуатации и сопровождению.

Требования к ПС оцениваются исходя из критериев соответствия имя требованиям к системе, реализуемости и возможности проверки при тестировании.

Проектирование архитектуры ПС включает следующие задачи (для каждого компонента ПС):

·        трансформацию требований к ПС в архитектуру, определяю­щую на высоком уровне структуру ПС и состав его компо­нентов;

·        разработку и документирование программных интерфейсов ПС

и баз данных;

·        разработку предварительной версии пользовательской докумен-

тации;

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

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

Детальное проектирование ПС включает следующие задачи:

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

·        разработку   и  документирование  детального   проекта   базы данных;

·        обновление (при необходимости) пользовательской докумен-тации;

·        разработку и документирование требований к тестам и плана тестирования компонентов ПС;

·        обновление плана интеграции ПС.

Кодирование и тестирование ПС охватывают следующие за­дачи:

  разработку (кодирование) и документирование каждого ком­понента ПС и базы данных, а также совокупности тестовых процедур и данных для их тестирования;

  тестирование каждого компонента ПС и базы данных на соот­ветствие предъявляемым к ним требованиям. Результаты тес­тирования компонентов должны быть документированы;

  обновление (при необходимости) пользовательской докумен­тации;

  обновление плана интеграции ПС.

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

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

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

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

Приемка ПС предусматривает оценку результатов квалификационного тестирования ПС и системы и документирование

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

Процесс эксплуатации (operation process) охватывает действия и задачи оператора — организации, эксплуатирующей систему (рис. 2.5).

Рис. 2.5. Схема процесса эксплуатации

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

· планирование действий и работ, выполняемых в процессе эксплуатации, и установку эксплуатационных стандартов;

· определение процедур локализации и разрешения проблем, возникающих в процессе эксплуатации.

Эксплуатационное тестирование осуществляется для каждой очередной редакции программного продукта, после чего она передается в эксплуатацию.

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

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

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

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

Рис. 2.6. Схема процесса сопровождения

Подготовительная работа службы сопровождения включает следующие задачи:

  планирование действий и работ, выполняемых в процессе сопровождения;

  определение процедур локализации и разрешения проблем, возникающих в процессе сопровождения.

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

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

  оценку целесообразности проведения модификации и возможных вариантов ее проведения;

  утверждение выбранного варианта модификации.

Модификация ПС предусматривает определение компонентов ПС  их версий и документации, подлежащих модификации, и вне-

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

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

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

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

 

3. Вспомогательные процессы жизненного цикла программного средства

Процесс документирования (documentation process) предусматривает формализованное описание информации, созданной в течении ЖЦ ПС. Данный процесс состоит из набора действий, с помощью которых планируют, проектируют, разрабатывают, выпускают, редактируют, распространяют и сопровождают документы, необходимые для всех заинтересованных лиц, таких, как руководители, технические специалисты и пользователи системы (рис.  2.7).э

Рис. 2.7. Схема процесса документирования

Процесс управления конфигурацией (configuration management process) предполагает применение административных и технических процедур на всем протяжении ЖЦ ПС для определения состояния компонентов ПС в системе, управления модификациями ПС, описа­ния и подготовки отчетов о состоянии компонентов ПС и запросов на модификацию, обеспечения полноты, совместимости и коррект­ности компонентов ПС, управления хранением и поставкой ПС. Со­гласно стандарту IEEE-90 под конфигурацией ПС понимается сово­купность его функциональных и физических характеристик, уста­новленных в технической документации и реализованных в ПС.

Управление конфигурацией позволяет организовать, система­тически учитывать и контролировать внесение изменений в ПС на всех стадиях ЖЦ (рис. 2.8).

Рис. 2.8. Схема процесса управления конфигурацией

Подготовительная работа заключается в планировании уп­равления конфигурацией.

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

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

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

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

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

Процесс обеспечения качества (quality assurance process) обес­печивает соответствующие гарантии того, что ПС и процессы его ЖЦ соответствуют заданным требованиям и утвержденным планам. Под качеством ПС понимается совокупность свойств, которые характеризуют способность ПС удовлетворять задан­ным требованиям (рис. 2.9).

Рис. 2.9. Схема процесса обеспечения качества

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

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

Обеспечение качества продукта подразумевает гарантирова­ние полного соответствия программных продуктов и документа­ции на них требованиям заказчика, предусмотренным в договоре.

Обеспечение качества процесса предполагает гарантирование соответствия процессов ЖЦ ПС, методов разработки, среды раз­работки и квалификации персонала условиям договора, установ­ленным стандартам и процедурам.

Обеспечение прочих показателей качества системы осуществ­ляется в соответствии с условиями договора и стандартом каче­ства ISO 9001.

Процесс верификации (verification process) состоит в определе­нии того, что программные продукты, являющиеся результата­ми некоторого действия, полностью удовлетворяют требовани­ям или условиям, обусловленным предшествующими действиями (верификация в «узком» смысле означает формальное доказатель­ство правильности ПС). Для повышения эффективности верифи­кация должна как можно раньше интегрироваться с использую­щими ее процессами (такими, как поставка, разработка, эксплуа­тация или сопровождение). Данный процесс может включать анализ, оценку и тестирование (рис. 2.10).

 

Рис. 2.10. Схема процесса верификации

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

В процессе верификации проверяются следующие условия:

  непротиворечивость требований к системе и степень учета по­требностей пользователей;

  возможности поставщика выполнить заданные требования;

  соответствие выбранных процессов ЖЦ ПС условиям договора;

  адекватность стандартов, процедур и среды разработки про­цессам ЖЦ ПС;

  соответствие проектных спецификаций ПС заданным требова­ниям;

  корректность описания в проектных спецификациях входных и выходных данных, последовательности событий, интерфей­сов, логики и т.д.;

  соответствие кода проектным спецификациям и требованиям;

  тестируемость и корректность кода, его соответствие приня­тым стандартам кодирования;

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

  адекватность, полнота и непротиворечивость документации.

Процесс аттестации (validation process) предусматривает оп­ределение полноты соответствия заданных требований и создан­ной системы или программного продукта их конкретному функ­циональному назначению. Под аттестацией обычно понимают­ся подтверждение и оценка достоверности проведенного тестирования ПС. Аттестация должна гарантировать полное со­ответствие ПС спецификациям, требованиям и документации, а также возможность его безопасного и надежного применения пользователем. Аттестацию рекомендуется выполнять путем тес­тирования во всех возможных ситуациях и использовать при этом независимых специалистов. Аттестация может проводиться на начальных стадиях ЖЦ ПС или как часть работы по приемке ПС (рис. 2.11).

Рис. 2.11. Схема процесса аттестации

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

Процесс совместной оценки (joint review process) предназначен для оценки состояния работ по проекту и ПС, создаваемому при вы­полнении данных работ (действий). Он сосредоточен в основном на контроле планирования и управления ресурсами, персоналом, ап­паратурой и инструментальными средствами проекта (рис. 2.12).

Рис. 2.12. Схема процесса оценки

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

Процесс аудита (audit process) представляет собой определе­ние соответствия требованиям, планам и условиям договора.

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

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

Рис. 2.13. Схема процесса аудита

Процесс разрешения проблем (problem resolution process) пре­дусматривает анализ и решение проблем (включая обнаружен­ные несоответствия) независимо от их происхождения или ис­точника, которые обнаружены в ходе разработки, эксплуатации, сопровождения или других процессов. Каждая обнаруженная проблема должна быть идентифицирована, описана, проанали­зирована и разрешена (рис. 2.14).

Рис. 2.14. Схема процесса разрешения проблем


 

 

4. Качество программного обеспечения

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

- надежность;

- эффективность;

- модифицируемость;

- мобильность;

- понятность (программа должна быть составлена так, чтобы ее легко было читать и использовать, так как программа пишется для людей);

   - учет человеческого фактора.

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

     Мобильность - программа является завершенной и машинонезависимой.

     Модифицируемость - это возможность расширения вычислительных возможностей отдельных модулей.

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

     Остановимся подробнее на характеристике надежности программного обеспечения.

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

o выдача неверных результатов;

o отсутствие результатов;

o уменьшение производительности;

o порча данных пользователя.

     Отказ программного продукта может быть обусловлен двумя причинами:

1) нарушение разработчиком программы спецификации - технических требований к программе;

2) спецификация неточная или не полная.

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

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

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

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

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

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

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

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

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

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

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

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

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

а) исходных данных, трансформированными в ходе вычислений;

б) округления;

в) погрешностями метода;

г) погрешностями, обусловленными отказами, сбоями и ошибками в программе;

     3) Резервирование программ (введение структурной избыточности).

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

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

     Рассмотренные способы резервирования требуют в 2 или N раз больше времени для вычислений и увеличение объема труда программистов во столько же раз.

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

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

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

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

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

 

 

5. Методы обеспечения надежности программных средств.

В со­временных автоматизированных технологиях создания и раз­вития сложных ПС с позиции обеспечения их необходимой и за­данной надежности можно выделить методы и средства, позво­ляющие:

  создавать программные модули и функциональные компоненты высокого, гарантированного качества;

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

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

  удостоверять достигнутое качество и надежность функциони­рования ПС в процессе их испытаний и сертификации перед передачей в регулярную эксплуатацию;

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

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

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

 

Предупреждение ошибок

 

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

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

2)  методы достижения большей точности при переводе;

3)  методы улучшения обмена информацией;

4)  методы немедленного обнаружения и устранения ошибок. Эти методы направлены на обнаружение ошибок на каждом шаге перевода, не откладывая до тестирования программы после ее написания.

Должно быть очевидно, что предупреждение ошибок — оп­тимальный путь к достижению надежности программного обес­печения.

Лучший способ обеспечить надежность — прежде всего не допустить возникновения ошибок. Гарантировать отсутствие ошибок, однако, невозможно никогда. Другие три группы мето­дов опираются на предположение, что ошибки все-таки будут.

 

Обнаружение ошибок

 

Если предполагать, что в программном обеспечении какие-то ошибки все же будут, то лучшая (после предупреждения ошибок) стратегия — включить средства обнаружения ошибок в само про­граммное обеспечение.

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

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

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

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

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

2. Немедленное обнаружение. Ошибки необходимо обнаружить как можно раньше. Это не только ограничивает наносимый ими ущерб, но и значительно упрощает задачу отладки.

3.  Избыточность. Все средства обнаружения ошибок основа­ны на некоторой форме избыточности (явной или неявной).

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

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

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

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

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

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

 

Исправление ошибок

 

Следующий шаг — методы исправления ошибок; после того как ошибка обнаружена, либо она сама, либо ее последствия дол­жны быть исправлены программным обеспечением. Исправление ошибок самой системой — плодотворный метод проектирова­ния надежных систем аппаратного обеспечения. Некоторые уст­ройства способны обнаружить неисправные компоненты и пе­рейти к использованию идентичных запасных. Аналогичные ме­тоды неприменимы к программному обеспечению вследствие глубоких внутренних различий между сбоями аппаратуры и ошиб­ками в программах. Если некоторый программный модуль со­держит ошибку, идентичные «запасные» модули также будут со­держать ту же ошибку.

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

 

Устойчивость к ошибкам

 

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

1. Истоки концепции динамической избыточности лежат в проектировании аппаратного обеспечения. Один из подходов к динамической избыточности — метод голосования. Данные об­рабатываются независимо несколькими идентичными устройства­ми, и результаты сравниваются. Если большинство устройств выработало одинаковый результат, этот результат и считается правильным. И опять, вследствие особой природы ошибок в про­граммном обеспечении ошибка, имеющаяся в копии программ­ного модуля, будет также присутствовать во всех других его ко­пиях, поэтому идея голосования здесь, видимо, неприемлема. Предлагаемый иногда подход к решению этой проблемы состоит в том, чтобы иметь несколько неидентичных копий модуля. Это значит, что все копии выполняют одну и ту же функцию, но либо реализуют различные алгоритмы, либо созданы разными разра­ботчиками. Этот подход бесперспективен по следующим причинам. Часто трудно получить существенно разные версии модуля, выполняющие одинаковые функции. Кроме того, возникает не­обходимость в дополнительном программном обеспечении для организации выполнения этих версий параллельно или последо­вательно и сравнения результатов. Это дополнительное программ­ное обеспечение повышает уровень сложности системы, что, ко­нечно, противоречит основной идее предупреждения ошибок — стремиться в первую очередь минимизировать сложность.

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

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

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

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

1.  Прикладная программа не должна иметь возможности не­посредственно ссылаться на другую прикладную программу или данные в другой программе и изменять их.

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

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

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

5.  Прикладные программы не должны иметь возможности ни остановить систему, ни вынудить ее изменить другую приклад­ную программу или ее данные.

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

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

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

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

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

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

Реализация многих из этих принципов влияет на архитектуру лежащего в основе системы аппаратного обеспечения.

 

Обработка сбоев аппаратуры

 

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

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

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

2. Восстановление памяти. Если обнаруженный случайный сбой аппаратуры вызывает искажение области основной памяти и эта область содержит статические данные (например, команды объек­тной программы), то последствия сбоя можно ликвидировать, повторно загрузив эту область памяти.

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

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

5.  Контрольная точка/рестарт. Контрольная точка — это периодически обновляемая копия состояния прикладной програм­мы или всей системы. Если происходит отказ аппаратуры, такой, как ошибка ввода-вывода, сбой памяти или питания, программа может быть запущена повторно с последней контрольной точки.

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

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

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

Важное обстоятельство, касающееся всех четырех подходов, состоит в том, что обнаружение, исправление ошибок и устойчи­вость к ошибкам в некотором отношении противоположны ме­тодам предупреждения ошибок. В частности, обнаружение, ис­правление и устойчивость требуют дополнительных функций от самого программного обеспечения. Тем самым не только увели­чивается сложность готовой системы, но и появляется возмож­ность внести новые ошибки при реализации этих функций. Как правило, все рассматриваемые методы предупреждения и многие методы обнаружения ошибок применимы к любому программ­ному проекту. Методы исправления ошибок и обеспечения ус­тойчивости применяются не очень широко. Это, однако, зависит от области приложения. Если рассматривается, скажем, система реального времени, то ясно, что она должна сохранить работос­пособность и при наличии ошибок, а тогда могут оказаться же­лательными и методы исправления и обеспечения устойчивости. К системам такого типа относятся телефонные переключатель­ные системы, системы управления технологическими процесса­ми, аэрокосмические и авиационные диспетчерские системы и операционные системы широкого назначения [51].

 

6. Тестирование программных средств. Комплексное тестирование

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

О состоянии дел лучше всего свидетельствует тот факт, что большинство людей, работающих в области обработки данных, даже не могу `т правильно определить понятие «тестирование», и это на самом деле главная причина неудач. Если попросить лю­бого профессионала определить понятие «тестирование» либо открыть (как правило, слишком краткую) главу о тестировании любого учебника программирования, то скорее всего можно встре­тить такое определение:

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

Тестирование процесс выполнения программы с намерением найти ошибки.

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

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

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

 

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

Часть аргументов в пользу этого должна быть уже очевид­ной: измеримые цели необходимы, чтобы определить правила для процессов проектирования. Остальные соображения должны про­ясниться сейчас. Если цели сформулированы, например, в виде требования, чтобы система была достаточно быстрой, вполне надежной и чтобы в разумных пределах обеспечивалась безопас­ность, тогда нет способа определить при тестировании, в какой степени система достигает своих целей.

Если вы не сформулировали цели вашего продукта или если эти цели неизмеримы, вы не можете выполнить комплексное тести­рование.

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

 

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

 

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

1. Тестирование стрессов. Распространенный недостаток боль­ших систем состоит в том, что они функционируют как будто бы нормально при слабой или умеренной нагрузке, но выходят из строя при большой нагрузке и в стрессовых ситуациях реальной среды. Тестирование стрессов представляет собой попытки под­вергнуть систему крайнему «давлению», например, попытку од­новременно подключить к системе разделения времени 100 тер­миналов, насытить банковскую систему мощным потоком вход­ных сообщений или систему управления — процессами аварийных сигналов от всех ее процессов.

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

2. Тестирование объема. В то время как при тестировании стрессов делается попытка подвергнуть систему серьезным нагруз­кам в короткий интервал времени, тестирование объема пред­ставляет собой попытку предъявить системе большие объемы данных в течение более длительного времени. На вход компиля­тора следует подать до нелепости громадную программу. Про­грамма обработки текстов — огромный документ. Очередь зада­ний операционной системы следует заполнить до предела. Цель тестирования объема — показать, что система или программа не может обрабатывать данные в количествах, указанных в их спе­цификациях.

3.  Тестирование конфигурации. Многие системы, например операционные системы или системы управления файлами, обес­печивают работу различных конфигураций аппаратуры и про­граммного обеспечения. Число таких конфигураций часто слиш­ком велико, чтобы можно было проверить все варианты. Однако следует тестировать по крайней мере максимальную и минималь­ную конфигурации. Система должна быть проверена со всяким аппаратным устройством, которое она обслуживает, или со вся­кой программой, с которой она должна взаимодействовать. Если сама программная система допускает несколько конфигураций (т.е. покупатель может выбрать только определенные части или варианты системы), должна быть тестирована каждая из них.

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

5.  Тестирование защиты. Так как внимание к вопросам сохра­нения секретности в обществе возрастает, к большинству систем предъявляются определенные требования по обеспечению защи­ты от несанкционированного доступа. Например, операционная система должна устранить всякую возможность для программы пользователя увидеть данные или программу другого пользова­теля. Административная информационная система не должна позволять подчиненному получить сведения о зарплате тех, кто стоит выше его по служебной лестнице. Цель тестирования за­щиты — нарушить секретность в системе. Один из методов нанять профессиональную группу «взломщиков», т. е. людей с опытом разрушения средств обеспечения защиты в системах. Для тестирования защиты важно построить такие тесты, которые нарушат программные средства защиты, например, механизм за­щиты памяти операционной системы или механизмы защиты дан­ных системы управления базой данных. Одним из путей разра­ботки подобных тестов является изучение известных проблем за­щиты в этих системах и генерация тестов, которые позволяют проверить, как решаются аналогичные проблемы в тестируемой системе.

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

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

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

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

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

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

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

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

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

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

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

Основное правило при комплексном тестировании — все го­дится. Пишите разрушительные тесты, проверяйте все функцио­нальные границы системы, пишите тесты, представляющие ошиб­ки пользователя (например, оператора системы). Нужно, чтобы тестовик думал так же, как пользователь или покупатель, а это предполагает доскональное понимание того, для чего система будет применяться. Поэтому возникает вопрос: «Кто же должен выполнять комплексное тестирование и, в частности, кто должен проектировать тесты?» Во всяком случае этого не должны делать программисты или организации, ответственные за разработку системы. Группа тестирования системы должна быть независи­мой организацией и должна включать профессиональных специ­алистов по комплексному тестированию систем; несколько пользо­вателей, для которых система разрабатывалась; основных анали­тиков и проектировщиков системы и, возможно, одного или двух психологов. Очень важно включить самих проектировщиков си­стемы. Это не противоречит аксиоме, согласно которой невоз­можно тестировать свою собственную систему, поскольку систе­ма прошла через много рук после того, как ее описали архитек­тор или проектировщик. В действительности проектировщик и не пытается тестировать собственную систему; он ищет расхож­дения между окончательной версией и тем, что он первоначаль­но, возможно год или два назад, имел в виду. Для комплексного тестирования желательно также иметь информацию о рынке или пользователях, уточняющую предположения о конфигурации и характере применения системы.

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

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

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

 

7. Тестирование модулей

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

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

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

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

 

Пошаговое тестирование

 

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

Возникает вопрос: «Что лучше — выполнить по отдельности тестирование каждого модуля, а затем, комбинируя их, сформи­ровать рабочую программу или же каждый модуль для тестиро­вания подключать к набору ранее оттестированных модулей?». Первый подход обычно называют монолитным методом, или ме­тодом «большого удара», при тестировании и сборке программы;

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

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

Детального разбора обоих методов мы делать не будем, при­ведем лишь некоторые общие выводы.

1.  Монолитное тестирование требует больших затрат труда. При пошаговом же тестировании «снизу-вверх» затраты труда сокращаются.

2.  Расход машинного времени при монолитном тестировании меньше.

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

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

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

6.  Результаты пошагового тестирования более совершенны.

В заключение отметим, что п. 1, 4, 5, 6 демонстрируют пре­имущества пошагового тестирования, а п. 2 и 3 — его недостат­ки. Поскольку для современного этапа развития вычислительной техники характерны тенденции к уменьшению стоимости аппаратуры и увеличению стоимости труда, последствия ошибок в математическом обеспечении весьма серьезны, а стоимость уст­ранения ошибки тем меньше, чем раньше она обнаружена; пре имущества, указанные в п. 1, 4, 5, 6, выступают на первый план. В то же время ущерб, наносимый недостатками (п. 2 и 3), неве­лик. Все это позволяет нам сделать вывод, что пошаговое тести­рование является предпочтительным.

Убедившись в преимуществах пошагового тестирования пе­ред монолитным, исследуем две возможные стратегии тестирова­ния: нисходящее и восходящее. Прежде всего внесем ясность в тер­минологию.

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

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

 

Восходящее тестирование

 

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

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

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

 

Нисходящее тестирование

 

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

При этом подходе немедленно возникают два вопроса: 1. «Что делать, когда тестируемый модуль вызывает модуль более низко­го уровня (которого в данный момент еще не существует)?» и 2. «Как подаются тестовые данные?»

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

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

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

Преимуществом нисходящего подхода очень часто считают отсутствие необходимости в драйверах; вместо драйверов вам просто следует написать «заглушки».

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

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

 

Метод «большого скачка»

 

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

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

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

 

Метод сандвича

 

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

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

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

 

Модифицированный метод сандвича

 

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

Hosted by uCoz