Файл: Отладка и тестирование программ: основные подходы и ограничения (Понятия тестирования и отладки и их классификация ).pdf
Добавлен: 23.04.2023
Просмотров: 490
Скачиваний: 2
СОДЕРЖАНИЕ
1. ТЕОРЕТИЧЕСКИЕ ОСНОВЫ ТЕСТИРОВАНИЯ И ОТЛАДКИ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
1.1. Понятия тестирования и отладки и их классификация
1.2. Принципы и ограничения тестирования и отладки
1.3. Жизненный цикл тестирования программного обеспечения
1.4. Цели и задачи тестирования программного обеспечения
2. СТРАТЕГИЯ ТЕСТИРОВАНИЯ И ОТЛАДКИ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
2.1. Методы тестирования программного обеспечения
2.2. Методы отладки программного обеспечения
3. Тестовые данные должны быть достаточно просты для проверки. Прямое следствие предыдущего принципа.
4. Тесты готовятся заранее, до выхода на машину. Это касается как исходных данных, так и ожидаемых результатов.[8] Реально в подавляющем большинстве случаев тесты придумываются на ходу, причем только исходные данные.
5. Первые тесты разрабатываются после получения задания на разработку программы до написания программного кода. Самые первые тесты следует продумать сразу же после постановки задачи до того, как начали писать программный код. Ранняя разработка тестов позволяет правильно понять поставленную задачу.[9] Даже в самых простых и, на первый взгляд, очевидных заданиях часто встречаются тонкости, которые сразу не видны, но становятся заметны, когда вы пытаетесь определить результат, соответствующий конкретным входным данным. Цель ранней разработки тестов – уточнить постановку задачи, выявить тонкие места.[15] Без этого есть риск написать программу, которая будет решать какую-то иную задачу, а не ту, которая была поставлена.
6. Перед началом тестирования следует сформулировать цели, которые должны быть достигнуты в ходе тестирования. В частности, набор тестов должен быть полон с точки зрения выбранных критериев полноты тестирования.[15] Независимо от применяемых критериев разработка тестов должна вестись систематически, по определенной методике.
7. В процессе тестирования необходимо фиксировать выполненные тесты и реально полученные результаты. К сожалению, обычной является ситуация, когда студент, получив задание, сразу же садится за компьютер, вводит некоторый программный текст, после нескольких перетрансляций избавляется от синтаксических ошибок, после чего запускает полученную программу, на ходу придумывает и подает на вход программы некие исходные данные, получает результаты, вводит новые данные, опять получает результаты и т. д.[16] Тестовые данные и результаты нигде не фиксируются. Для любых нетривиальных программ подобное тестирование почти бесполезно, поскольку не позволяет отделить проверенные участки от непроверенных, не позволяет оценить количество ошибок в программе, не дает информации для принятия решения об окончании тестирования.
8. Тесты должны быть одинаково тщательны как для правильных, так и для неправильных входных данных. На практике часто ограничиваются тестированием правильных входных данных, забывая о неправильных.
9. Необходимо проверить два момента: программа делает то, что должна делать; программа не делает того, чего делать не должна.[8] Особенно это важно для изменений в глобальной среде. Если программа выдает правильные результаты, но при этом затирает половину винчестера, то едва ли ее можно признать правильной.
10. Результаты теста необходимо изучать досконально и объяснять полностью.
11. Недопустимо ради упрощения тестирования изменять программу. Тестировать после этого пользователь будет уже другую программу.
12. После исправления программы необходимо повторное тестирование.
Для того чтобы исправить обнаруженную ошибку, мы вносим изменения в программу. Но кто может гарантировать, что, исправив одну ошибку, мы не внесем другую. Вероятность внесения новой ошибки при исправлении старой оценивается в 20–50%.[13] Для особо сложных систем эта вероятность может быть значительно выше. Так, в знаменитой в свое время ОС IBM/360 количество ошибок считалось постоянным и оценивалось примерно в 1000.[9] Система была настолько сложна и запутанна, что считалось невозможным «починить» ее в одном месте и при этом не «сломать» в другом. Итог: после внесения изменений в программу необходимо заново прогнать весь пакет ранее выполненных тестов.
13. Ошибки кучкуются. Чем больше ошибок обнаружено в модуле, тем больше вероятность, что там есть еще. Так, в одной из версий системы 370 (преемника IBM/360) 47% обнаруженных ошибок пришлось на 4% модулей [1].
Это утверждение противоречит здравому смыслу. «Здравый смысл» неявно исходит из предположения о равномерном распределении ошибок по всему тексту программы. На чем основано такое предположение. На взгляде на программу как на некую однородную сущность, все части которой обладают примерно одинаковыми свойствами. Реально программа устроена гораздо более сложно. В ней есть фрагменты простые и фрагменты сложные. Есть функции, которые были хорошо специфицированы, и функции, для которых спецификации были сформулированы нечетко. Есть модули, которые были спроектированы добротно, и модули, которые были спроектированы небрежно. Есть части, которые писали опытные программисты, и части, которые писали новички. Естественно, что большая часть ошибок окажется в тех частях, которые более сложны, хуже специфицированы, хуже спроектированы, написаны новичками. С учетом этих условий кучкование ошибок уже не выглядит странным.[6]
Данный принцип имеет одно неприятное последствие. Если следовать ему строго, то количество ошибок в программе должно возрастать до бесконечности. Ведь нахождение каждой следующей ошибки увеличивает вероятность существования других еще ненайденных ошибок. К счастью, это не так.
14. Окончательное тестирование программы лучше проводить не ее автору, а другому человеку.
Тестирование программы – процесс разрушительный. Цель его – выявление дефектов в программе. Психологически любой создатель всегда склонен в большей или меньшей степени отождествлять себя со своим созданием, «вкладывать в него душу».[8] И чем больше усилий потрачено на работу, тем больше степень отождествления. Программа начинает восприниматься программистом как продолжение его самого. В восприятии автора обнаружение ошибок в программе приобретает трагический оттенок: «Программа – это продолжение меня. Программа – дефектна. Следовательно, я дефектен!». Если вы не склонны к мазохизму, такая ситуация вам вряд ли понравится.
Необходимо либо передать окончательное тестирование вашей программы другому человеку. Либо четко отделить себя от программы, перестать воспринимать ее как часть себя (не «моя программа», а «программа, написанная мной»). Это, может быть, проще сделать в программистском коллективе, в котором все программы являются результатом коллективной работы и коллективной собственностью.[10] Но то же самое необходимо и при одиночной работе. Не отделив себя от своего детища, вы не сможете объективно оценить его достоинства и недостатки.
Естественно, что человек, проверяющий вашу программу, должен быть достаточно подготовлен, готов потратить на эту нужное количество времени и усилий. Это не сможет сделать первый встречный. Это нельзя сделать мимоходом.
1.3. Жизненный цикл тестирования программного обеспечения
Жизненный цикл тестирования выражается замкнутой последовательностью действий (рисунок 1).[5]
Рисунок 1 – Жизненный цикл тестирования
Важно понимать, что длина такой итерации (и, соответственно, степень подробности каждой стадии) может варьироваться в широчайшем диапазоне – от единиц часов до десятков месяцев. Как правило, если речь идёт о длительном промежутке времени, он разбивается на множество относительно коротких итераций, но сам при этом «тяготеет» к той или иной стадии в каждый момент времени (например, в начале проекта больше планирования, в конце – больше отчётности).[3]
Приведённая схема – не догма, и вы легко можете найти альтернативы, но общая суть и ключевые принципы остаются неизменными.
Стадия 1 (общее планирование и анализ требований) объективно необходима как минимум для того, чтобы иметь ответ на такие вопросы, как: что нам предстоит тестировать; как много будет работы; какие есть сложности; всё ли необходимое у нас есть и т.п. Как правило, получить ответы на эти вопросы невозможно без анализа требований, т.к. именно требования являются первичным источником ответов.
Стадия 2 (уточнение критериев приёмки) позволяет сформулировать или уточнить метрики и признаки возможности или необходимости начала тестирования, приостановки и возобновления тестирования, завершения или прекращения тестирования.[5]
Стадия 3 (уточнение стратегии тестирования) представляет собой ещё одно обращение к планированию, но уже на локальном уровне: рассматриваются и уточняются те части стратегии тестирования, которые актуальны для текущей итерации.
Стадия 4 (разработка тест-кейсов) посвящена разработке, пересмотру, уточнению, доработке, переработке и прочим действиям с тест-кейсами, наборами тест-кейсов, тестовыми сценариями и иными артефактами, которые будут использоваться при непосредственном выполнении тестирования.
Стадия 5 (выполнение тест-кейсов) и стадия 6 (фиксация найденных дефектов) тесно связаны между собой и фактически выполняются параллельно: дефекты фиксируются сразу по факту их обнаружения в процессе выполнения тест-кейсов.[5] Однако зачастую после выполнения всех тест-кейсов и написания всех отчётов о найденных дефектах проводится явно выделенная стадия уточнения, на которой все отчёты о дефектах рассматриваются повторно с целью формирования единого понимания проблемы и уточнения таких характеристик дефекта, как важность и срочность.
Стадия 7 (анализ результатов тестирования) и стадия 8 (отчётность) также тесно связаны между собой и выполняются практически параллельно. Формулируемые на стадии анализа результатов выводы напрямую зависят от плана тестирования, критериев приёмки и уточнённой стратегии[3], полученных на стадиях 1, 2 и 3. Полученные выводы оформляются на стадии 8 и служат основой для стадий 1, 2 и 3 следующей итерации тестирования. Таким образом, цикл замыкается.
1.4. Цели и задачи тестирования программного обеспечения
Цели тестирования заключаются в следующем:
- Повысить вероятность того, что приложение, предназначенное для тестирования, будет работать правильно при любых обстоятельствах.
- Повысить вероятность того, что приложение, предназначенное для тестирования, будет соответствовать всем описанным требованиям.
- Провести полное тестирование приложения за короткий срок.[5]
Задачи тестирования состоят в том, чтобы:
- Проверить, что система работает в соответствии с определенными временами отклика клиента и сервера.
- Проверить, что наиболее критические последовательности действий с системой конечного пользователя выполняются верно.
- Проверить работу пользовательских интерфейсов
- Проверить, что изменения в базах данных не оказывают неблагоприятного влияния на существующие программные модули.
- При проектировании тестов свести к минимуму переработку тестов при возможных изменениях приложения.
- Использовать инструменты автоматизированного тестирования там, где это целесообразно.
- Проводить тестирование таким образом, чтобы не только обнаруживать, но и предупреждать дефекты.
- При проектировании автоматизированных тестов использовать стандарты разработки таким образом, чтобы создать многократно используемые и сопровождаемые скрипты.[8]
2. СТРАТЕГИЯ ТЕСТИРОВАНИЯ И ОТЛАДКИ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
2.1. Методы тестирования программного обеспечения
Для того чтобы процесс тестирования имел оправданную с экономической точки зрения трудоемкость, необходимо заранее выработать ряд стратегий.
Долгое время основным способом тестирования было тестирование методом «черного ящика» - программе подавались некоторые данные на вход и проверялись результаты в надежде найти несоответствия.[5] При этом, как именно работает программа, считается несущественным. Следует ответить, что даже при таком подходе необходимо иметь спецификацию программы для того, чтобы было с чем сравнивать результаты.
Этот подход до сих пор является самым распространенным в повседневной практике, но у него есть целый ряд недостатков. Во-первых, таким способом невозможно найти взаимоуничтожающихся ошибок, во-вторых, некоторые ошибки возникают достаточно редко (ошибки работы с памятью) и потому их трудно найти и воспроизвести и т.д.[8]
В связи с этим появились методы тестирования, которые изучают не только внешнее поведение программы, но и ее внутреннее устройство (исходные тексты). Такие методики обобщенно называют тестированием «белого ящика». К ним относятся: обзоры кода, инспекции, аудит, критический анализ и т.д.[9] Основной трудностью подобных методов является сложность отслеживания вычислений времени выполнения.
Внимательное изучение этих методов тестирования показывает, что они дополняют друг друга, то есть различные методы находят разные ошибки. Поэтому наиболее эффективные процессы разработки ПО используют некоторую комбинацию методик «черного ящика» и «белого ящика».
Метод белого ящика (white box testing, open box testing, clear box testing, glass box testing) – у тестировщика есть доступ к внутренней структуре и коду приложения, а также есть достаточно знаний для понимания увиденного. Выделяют даже сопутствующую тестированию по методу белого ящика глобальную технику – тестирование на основе дизайна (design-based testing).[15] Для более глубокого изучения сути метода белого ящика рекомендуется ознакомиться с техниками исследования потока управления или потока данных, использования диаграмм состояний. Некоторые авторы склонны жёстко связывать этот метод со статическим тестированием, но ничто не мешает тестировщику запустить код на выполнение и при этом периодически обращаться к самому коду (а модульное тестирование и вовсе предполагает запуск кода на исполнение и при этом работу именно с кодом, а не с «приложением целиком»).