Материал: Технол_разраб_прогр_обесп_Гагарина_Кокарева

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Пример 4.5. Разработать диаграмму коопераций, иллюстрирующую взаимоотношения компании с составляющими ее отделами и сотрудниками.

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

Решение приведено на рис. 4.26.

С : Компания



    1. Создать

    2. Выдать задание

    3. Расформировать


    2.3. Сдать 4. Принять отчет 5. Перевести в другой отдел 6. Уволить


    Выдать задание

  1. Принять работу

: Отдел

Сотрудник

2.1.1. Выполнить

работу

2.1.2. Сдать отчет Рис. 4.26. Пример диаграммы кооперации


Заметим, что на приведенной диаграмме объекты классов «Отдел» и «Сотрудник» реализованы мультиобъектами.

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



4.3. Экстремальное программирование


4.3.1. Основополагающие практики ХР


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

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

В основе ХР лежит предположение, что эту кривую можно сгладить до такой степени, чтобы можно было применять эволюционное проектирование. Такое сглаживание, с одной стороны, возникает при использовании методологии ХР, а с другой — оно же в ней и используется. Это еще раз подчеркивает тесную взаимосвязь между практиками ХР: нельзя использовать те части методологии, которые предполагают существование сглаживания, не используя те практики, которые это сглаживание осуществляют.

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

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

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

4.3.2. Преимуществе простого дизайна


В ХР очень популярны два лозунга: «Do the Simplest Thing that Could Possibly Work» («Ищите самое простое решение, которое может сработать») и YAGNI («You Aren't Going to Need It» — «Это вам не понадобится»). Оба они олицетворяют собой одну из практик ХР под названием Простой дизайн [3].

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

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

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

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

Другая причина, по которой простой дизайн лучше сложного, — отказ от принципа «блуждающего огонька». Сложную конструкцию гораздо труднее понять, чем простую. Именно поэтому любая модификация системы делает ее все более сложной. Это опять-таки ведет к увеличению стоимости работ в период между тем временем, когда дизайн системы стал более сложным, и временем, когда это действительно стало необходимо [3].

Такой стиль работы многим кажется абсурдным, и надо сказать, что они правы. Правы при одном условии — абсурд получится, если эту практику начать применять в обычном процессе разработки, а все остальные практики ХР игнорировать. Если же изменить существующий баланс между эволюционным и предварительным проектированием, то YAGNI становится очень полезным принципом (тогда и только тогда).

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


4.3-3. Простой дизайн


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

В книге Extreme Programming Explained Кент приводит четыре критерия простой системы [20]. Вот они в порядке убывания важности:

• система успешно проходит все тесты;

  • код системы ясно раскрывает все изначальные замыслы;

  • в ней отсутствует дублирование кода;

  • используется минимально возможное количество классов и методов.

Успешное тестирование системы — довольно простой критерий. Отсутствие дублирования кода тоже вполне четкое требование, хотя большинство разработчиков нужно учить, как этого достичь. Самое сложное скрывается в словах «раскрывает изначальные замыслы». Что это значит?

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


4.3-4. Рефакторинг и принцип УАСЫ1


Эта тема сравнительно недавно всплыла в списке рассылки, посвященном ХР, и, коль скоро мы заговорили о роли проектирования, нам стоит ее обсудить.

Дело в том, что процесс рефакторинга требует времени, но не добавляет новой функциональности. С другой стороны, принцип УАОМ гласит, что надо проектировать только для текущей функциональности, а не для того, что понадобится в будущем. Не сталкиваемся ли мы здесь с противоречием?

Принцип УАСМ состоит в том, чтобы не делать систему более сложной, чем того требует реализация текущих задач. Это является частью практики «Простой дизайн». Рефакторинг же необходим для поддержания системы в максимально простом состоянии. Его нужно проводить сразу же, как только вы обнаружите, что можете что-либо упростить.

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


4.3-5- Наращивание архитектуры


Термин «архитектура» передает идею основных элементов системы, тех ее частей, которые трудно изменить. Они являются фундаментом, на котором можно построить все остальное [6].

Какую роль играет архитектура в эволюционном проектировании? Критики ХР считают, что эта методология вообще не признает работы над архитектурой, что вся суть ХР — сразу садиться за написание кода и уповать на то, что рефакторинг решит все проблемы с проектированием. Они правы, и, может быть, в этом заключается некоторая слабость ХР Приверженцы ХР — Кент Бек (Kent Beck), Рон Джеффриз (Ron Jeffries) и Боб Мартин (Bob Martin) — прикладывают очень много сил, чтобы вообще избежать любого предварительного проектирования архитектуры. Не добавляйте в систему базу данных, пока она вам действительно не понадобилась. Работайте поначалу с файлами, а база данных появится в следующей итерации, в результате рефакторинга.

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

4.3.6. иМ1 и ХР

В идеале ХР полностью отрицает проектирование системы, в частности методами 1ЖЬ. Тем не менее программисты все же часто используют на начальном этапе диаграммы иМЬ. На самом деле диаграммы очень полезны для понимая разрабатываемого продукта, но чтобы они сделали процесс более длительным и трудоемким, необходимо их использовать правильно [2, 25].

Советы тем, кто хочет правильно использовать диаграммы.

Во-первых, пока рисуете диаграмму, не забывайте, для чего вы это делаете. Основное ее достоинство — коммуникация с людьми. Чтобы коммуникация была эффективной, нужно отображать на диаграмме только важные аспекты, не обращая внимания на все второстепенные. Такая избирательность — основа правильной работы с 1ЛУ1Ь. Не надо отображать на диаграмме каждый класс — только самые важные. У классов не нужно задавать каждый атрибут или операцию — только самые важные. Не надо рисовать диаграммы последовательности для всех вариантов использования и сценариев — ну, и так далее. Самая распространенная проблема с использованием диаграмм — это то, что их пытаются сделать максимально всеобъемлющими. Однако самый лучший источник всеобъемлющей информации — это программный код, так как именно его легче всего синхронизировать с кодом. Для диаграммы же всеобъемлемость — враг удобопонятности.

Чаще всего диаграммы используются для того, чтобы проанализировать проектные решения еще до написания кода. Нередко при этом возникает чувство, что в ХР этого делать нельзя. Это совсем не так. Многие полагают, что перед разработкой сложной задачи стоит ненадолго собраться всей командой для ее предварительного проектирования. Тем не менее, когда проводите такие собрания, не забывайте, что:

  • они должны быть действительно недолгими;

  • не нужно обсуждать все подробности (только самое важное);

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

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

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

Кроме того, иМЬ-диаграммы используются в качестве документации по проекту. Как правило, в своей обычной форме это модель, редактируемая с помощью некоторого СА8Е-инстру-мента. Идея здесь состоит в том, что ведение такой документации облегчает работу. На самом деле, чаще всего она вообще не нужна, поскольку:

  • нужно постоянно тратить массу времени, чтобы не дать диаграммам устареть, в противном случае они не будут соответствовать программному коду;

  • диаграммы находятся внутри сложного СА8Е-средства либо в толстенной папке, и никто туда не заглядывает.

Итак, если вы хотите иметь текущую документацию по проекту, учитывайте все вышеперечисленные проблемы:

  • используйте только те диаграммы, которые вы можете поддерживать без особых усилий;

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

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

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


4.3.7. Суть проектирования. Программирование и тестирование


Проектирование в ХР требует от человека следующих качеств:

  • постоянного желания сохранять программный код простым и понятным насколько это возможно;

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

  • хорошего знания паттернов: рассматривать их не просто как готовые решения, а оценивать своевременность и использовать постепенно, от простого к сложному;

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

Для того чтобы реализовать задачу, ответственный за нее программист прежде всего ищет себе партнера, поскольку окончательный код всегда пишется двумя людьми на одной машине. Если возникают вопросы о предмете или методах реализации, партнеры проводят короткую (15-минутную) встречу с заказчиком и/или программистами, осведомленными в вопросах кодирования задач, которые с наибольшей вероятностью будут связаны с кодом данной задачи в ходе реализации [1, 5].

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

• мы знаем простой способ заставить его работать, и мы действуем этим способом;

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

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

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

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

Контрольные вопросы

  1. Приведите пример структурной схемы ПО.

  2. Опишите основные элементы функциональных схем ПО.

  3. Охарактеризуйте метод пошаговой детализации.

  4. Как составляются структурные карты Константайна?

  5. Как составляются структурные карты Джексона?

  6. Что такое САБЕ-технологии?

  7. Что такое РАО-технологии?

  8. Охарактеризуйте модель проектируемого ПО при объектном подходе.

  9. Что такое экстремальное программирование?


Глава 5

ТЕСТИРОВАНИЕ И ОТЛАДКА ПРОГРАММ








В целом разработчики различают дефекты программного обеспечения и сбои. В случае сбоя программа ведет себя не так, как ожидает пользователь. Дефект — это ошибка/неточность, которая может быть (а может и не быть) следствием сбоя.

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

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

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

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

  • системное тестирование. Тестируется интегрированная система на ее соответствие исходным требованиям:

— альфа-тестирование — имитация реальной работы с системой штатными разработчиками либо реальная работа с системой потенциальными пользователями/заказчиком на стороне разработчика. Часто альфа-тестирование применяется для законченного продукта в качестве внут

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

5.1. Термины и определения

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

Таблица 5.1. Виды программных ошибок и способы их обнаружения

Виды программных ошибок

Способы их обнаружения

Синтаксические

Статический контроль и диагностика компиляторами и компоновщиком

Ошибки выполнения, выявляемые автоматически:

а) переполнение, защита памяти;

б) несоответствие типов;

в) зацикливание

Динамический контроль:

аппаратурой процессора; run-time системы программирования; операционной системой — по превышению лимита времени

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

Целенаправленное тестирование

Спецификация не соответствует требованиям

Испытания, бета-тестирование

Эффективность контроля 1-го вида зависит и от языка, и от компилятора. Контроль 2-го вида осуществляется с помощью исключений — Exceptions и весьма полезен для проверки правдоподобности промежуточных результатов. Тест — это набор контрольных входных данных совместно с ожидаемыми результатами. В число входных данных времязависимых программ входят события и временные параметры. Ключевой вопрос — полнота тестирования: какое количество каких тестов гарантирует, возможно, более полную проверку программы? Исчерпывающая проверка на всем множестве входных данных недостижима. Пример: программа, вычисляющая функцию двух переменных: Y=f(X, Z). Если X, Y, Z — real, то полное число тестов (232)2 = 264 « 1031 Если на каждый тест тратить 1 мс, то 264 мс = = 800 млн лет. Следовательно:

  • в любой нетривиальной программе на любой стадии ее готовности содержатся необнаруженные ошибки;

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

Детективность: тест должен с большой вероятностью обнаруживать возможные ошибки

Покрывающая способность: один тест должен выявлять как можно больше ошибок.

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

Только на основании выбранного критерия можно определить тот момент времени, когда конечное множество тестов окажется достаточным для проверки программы с некоторой полнотой (степень полноты, впрочем, определяется экспериментально). Используется два вида критериев (табл. 5.2):

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

  • структурные тесты составляются исходя из текста программы.

На рис. 5.1, а видно отличие тестирования команд (достаточен один тест) от С1 (необходимы два теста как минимум). Рисунок 5.1, б иллюстрирует различие С1 (достаточно двух тестов, покрывающих пути 1, 4 или 2, 3) от С2 (необходимо четыре теста для всех четырех путей). С2 недостижим в реальных програм-

Рис. 5.1. Траектории вычислений при структурном тестировании

мах из-за их цикличности, поэтому ограничиваются тремя путями для каждого цикла: 0, 1 и ТУ повторений цикла.

Остаются проблемы назначения классов входных/выходных данных для функционального тестирования и проектирования тестов для структурного тестирования. Классы, как правило, назначаются исходя из семантики решаемой задачи [6].

Рассмотрим пример. Найти минимальный набор тестов для программы нахождения вещественных корней квадратного уравнения ах2 + Ъх + с - 0.

Решение представлено в табл. 5.3.

Таблица 5.3. Поиск численного решения минимального набора тестов



Номер теста

а

b

с

Ожидаемый результат

Что проверяется

1

2

-5

2

xi = 2,х2 = 0,5

Случай вещественных корней

2

3

2

5

Сообщение

Случай комплексных корней

3

3

-12

0

jc[ = 4, jc2 = 0

Нулевой корень

4

0

0

10

Сообщение

Неразрешимое уравнение

5

0

0

0

Сообщение

Неразрешимое уравнение

6

0

5

17

Сообщение

Неквадратное уравнение (деление на 0)

7

9

0

0

jc[ = jc2 = 0

Корень из 0


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



5.2. Тестирование «белого ящика» и «черного ящика»


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

При тестировании «белого ящика» (англ. white-box testing, также говорят — прозрачного ящика) разработчик теста имеет доступ к исходному коду и может писать код, который связан с библиотеками тестируемого ПО. Это типично для юнит-тестирования (англ. unit testing), при котором тестируются только отдельные части системы. Оно обеспечивает то, что компоненты конструкции работоспособны и устойчивы до определенной степени.

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

Если альфа- и бета-тестирование относятся к стадиям до выпуска продукта (а также, неявно, к объему тестирующего сообщества и ограничениям на методы тестирования), тестирование «белого ящика» и «черного ящика» имеет отношение к способам, которыми тестировщик достигает цели.

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



5.3. Порядок разработки тестов


По внешней спецификации разрабатываются тесты [3]:

  • для каждого класса входных данных;

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

проверяются, и добавляются при необхопимости нужные тесты.

Разрабатываются тесты для тех функций, которые не проверяются в п. 1.

По тексту программы проверяется, все ли условные переходы выполнены в каждом направлении (С1). При необходимости добавляются новые тесты.

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

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

Функциональное тестирование дополняется здесь структурным. Классы входных/выходных данных должны быть определены в плане тестирования уже во внешней спецификации. Согласно статистике 1-й и 2-й пункты обеспечивают степень охвата С1 в среднем 40—50 %. Проверка по С1 (пункт 3) обычно выявляет 90 % всех ошибок, найденных при тестировании. (Все программное обеспечение ВВС США принимается с проверкой по С1.)

Систематическое тестирование предполагает также ведение журнала отладки (Bug Book), в котором фиксируется ошибка (описание, дата обнаружения, автор модуля) и в дальнейшем — исправление (дата, автор).

Приведем так называемые аксиомы тестирования.

  1. Тест должен быть направлен на обнаружение ошибки, а не на подтверждение правильности программы.

  2. Автор теста — не автор программы.

  3. Тесты разрабатываются одновременно или до разработки программы.

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

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

  6. Следует повторять полное тестирование после внесения изменений в программу или после переноса ее в другую среду.

  7. В те программы, в которых обнаружено много ошибок, необходимо дополнить первоначальный набор тестов [6].


5.4. Автоматизация тестирования


А. Автоматизация прогона тестов актуальна для 5-й и 6-й аксиом Майерса. Пишутся командные файлы для запуска программы с каждым тестом из набора и сравнением реального результата с ожидаемым. Существуют специальные средства (например система MIL-S для PL/1 фирмы IBM). Разрабатывается стандарт ШЕЕ скриптового языка для описания тестовых наборов [3].

Б. Средства автоматизации подготовки тестов и анализа их результатов.

1. Генераторы случайных тестов в заданных областях вход-
ных данных.

2. Отладчики (для локализации ошибок).

  1. Анализаторы динамики (profilers). Обычно входят в состав отладчиков; применяются для проверки соответствия тестовых наборов структурным критериям тестирования.

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



5.5. Модульное тестирование


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

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

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

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

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

Являясь по способу исполнения структурным тестированием или тестированием «белого ящика», модульное тестирование характеризуется степенью, в которой тесты выполняют или покрывают логику программы (исходный текст). Тесты, связанные со структурным тестированием, строятся по следующим принципам:

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

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

Тестирование на основе потока управления. Особенности использования структурных критериев тестирования СО, CI, С2 были рассмотрены в разд. 5.2. К ним следует добавить критерий покрытия условий, заключающийся в покрытии всех логических (булевых) условий в программе. Критерии покрытия решений (ветвей — С1) и условий не заменяют друг друга, поэтому на практике используется комбинированный критерий покрытия условий/решений, совмещающий требования по покрытию и решений, и условий.

К популярным критериям относится критерий покрытия функций программы, согласно которому каждая функция программы должна быть вызвана хотя бы 1 раз, и критерий покрытия вызовов, согласно которому каждый вызов каждой функции в программе должен быть осуществлен хотя бы 1 раз. Критерий покрытия вызовов известен также как критерий покрытия пар вызовов (call pair coverage).

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

Стратегия требуемых пар [15] также тестирует упомянутые взаимосвязи. Использование переменной в предикате дублируется в соответствии с числом выходов решения, и каждая из таких требуемых взаимосвязей должна быть протестирована. К популярным критериям принадлежит критерий CP, заключающийся в покрытии всех таких пар дуг v и w, что из дуги v достижима дуга w, поскольку именно на дуге может произойти потеря значения переменной, которая в дальнейшем уже не должна использоваться. Для покрытия еще одного популярного критерия Cdu достаточно тестировать пары (вершина, дуга), поскольку определение переменной происходит в вершине УГП, а ее использование — на дугах, исходящих из решений, или в вычислительных вершинах.

Методы проектирования тестовых путей для достижения заданной степени тестированности в структурном тестировании.

Источник: https://files.student-it.ru/previewfile/6671