|
|
|
содержание .. 1 2 3 ..
Тестирование в программной инженерии (лекция)
Тестирование, это процесс исследования, испытания программного продукта, имеющий две, по сути противоположные цели: · Выявить ситуации, в которых поведение программы является неправильным или не соответствующим спецификации; ·
Продемонстрировать разработчикам и
заказчикам, что программа соответствует В каждой программе могут быть, и скорее всего есть, ошибки. От этапа, на котором ошибка обнаруживается, зависит стоимость ее исправления. Барри Боэм (Barry Boehm), в своей книге «Software Engineering Economics», приводит сравнительные цифры стоимости обнаружения ошибок на различных этапах. На этапе определения требований – 1$, проектирования – 5$, разработки – 20$, тестирования – 50$, внедрения и эксплуатации – 100$. Цифры хоть и довольно условны, но ясно показывают пропорции. Тестирование не может доказать, что ошибки полностью отсутствуют в программе, однако соблюдение основных правил тестирования и грамотный подбор тестов позволяет уменьшить их количество. Не существует единых стандартизованных правил тестирования, но существует ряд общих рекомендаций и методик. Основные принципы: · Следует избегать тестирования программы автором; · Должны быть известны предполагаемые результаты, до начала тестирования; · Необходимо досконально изучать все результаты, полученные в ходе тестирования; · Необходимо проверять действия программы на неверных данных и на неожиданные побочные эффекты; Тестирование является самым дорогостоящим и трудоемким этапом разработки ПО. Фредерик Брукс (Frederick Brooks), в своей книге «Мифический человеко-месяц», приводит его практическое правило: 1/3 времени на проектирование, 1/6 на написание программы, 1/4 на компонентное тестирование и 1/4 на системное тестирование. Причем, доля стоимости тестирования в общей стоимости разработки имеет тенденцию возрастать при увеличении сложности программного обеспечения и повышении требований к их качеству. В соответствии с определением тестирования удачным следует считать тест, который обнаруживает хотя бы одну ошибку, либо доказывает, что при данном сценарии поведения, или входных данных, ошибки отсутствуют. Соответственно, наборы тестов должны быть оптимально подобраны для каждой конкретной задачи. Следует также иметь в виду, что вероятность наличия необнаруженных ошибок в части программы пропорциональна количеству ошибок, уже найденных в этой части. Выделяют две базовые функции тестирования: ·
Валидация (Validation)
– проверка продукта, сервиса или системы, на соответствие заявленным требованиям
со стороны заказчика. ·
Верификация (Verification)
– оценка продукта, сервиса или системы, на соответствие принятым внутренним
правилам и требованиям. Эти функции похожи очень во многом, но имеют разную направленность. Верификация производится после каждого этапа жизненного цикла ПО и отвечает на вопрос «строим ли мы продукт правильно?». Следующий этап не может начаться пока не будет пройдена верификация. Валидация, как правило, производится на этапе тестирования и отвечает на вопрос «строим ли мы правильный продукт?». Если продукт не соответствует заявленным требованиям, то возвращаются (либо ненадолго обращаются) к предыдущему этапу, с целью внесения доработок/исправлений. В самом общем виде, тестирование можно разделить на два вида: · Статическое тестирование – анализ требований, спецификаций, документации, программного кода, описаний архитектуры. 65-80% ошибок выявляется при интенсивном применении технологий статического тестирования еще на этапе разработки проекта; · Динамическое тестирование – проверка работы модулей или всего программного продукта в процессе эксплуатации. 20-35% остальных скрытых ошибок должны быть обнаружены на этапах тестирования модулей, различного вида испытаниях и сопровождении. Критерии завершения тестирования и отладки. Одним из самых сложных является вопрос о том, когда следует завершать тестирование, поскольку невозможно гарантировать, что в разрабатываемом программном обеспечении не осталось ошибок. Предложено очень много критериев. Все критерии можно разделить на две группы: · Основанные на методологиях проектирования тестов – определенное количество тестов, полученных по методам анализа причинно-следственных связей, анализа граничных значений и предположения об ошибке, перестают выявлять ошибки; · Основанные на
исследовании результатов тестирования – строят график
зависимости количества обнаруженных ошибок от времени тестирования, если он
напоминает график, представленный на рисунке ниже, то тестирование можно
завершать.
Часто тестирование завершают потому, что закончилось время, отведенное на выполнение данного этапа. В этом случае тестирование сворачивают, обходясь минимальным вариантом. Часть ошибок при этом остаются неисправленными «отложенными» до выпуска следующей версии. Минимальное тестирование предполагает: · Тестирование граничных значений; · Тестирование минимальных конфигураций технических средств; · Тестирование возможности выполнения команд в любой последовательности; · Тестирование устойчивости к ошибкам пользователя. Тема 3.1. Статическое тестированиеВсе проектные решения, принятые на том или ином этапе, должны анализироваться с точки зрения их правильности и целесообразности как можно раньше, пока их можно легко пересмотреть. Поскольку возможность практической проверки подобных решений на ранних этапах разработки отсутствует, большое значение имеет их исследование и обсуждение. Исходными данными для этого являются: · Техническое задание; · Спецификации; · Схемы отдельных компонентов; · Схемы программного продукта; · Исходный код программы. Статическое тестирование, как следует из перечня исходных данных, в первую очередь используют на ранних этапах разработки. Проверка исходного кода программы, которая так же называется Просмотр Кода (Code Review), или Инспекция Кода (Code Inspection), обычно проводится систематически. Целями данной проверки являются: выявление и исправление ошибок, улучшение качества программного продукта, совершенствование навыков разработчика. Инспекции исходного текста представляют собой набор процедур и приемов обнаружения ошибок при изучении текста группой специалистов. В эту группу входят: автор программы, проектировщик, специалист по тестированию и координатор – компетентный программист, но не автор программы. Зачастую, роль проектировщика и координатора исполняет один человек. Общая процедура инспекции предполагает следующие операции: · Участникам группы заранее выдается исходный код программы и спецификация на нее; · Программист рассказывает о логике работы программы, отвечает на вопросы инспекторов; · Программа анализируется по списку вопросов для выявления исторически сложившихся общих ошибок программирования, и специфичных для предметной области проекта. Список вопросов для инспекций исходного текста зависит, как от используемого языка программирования, так и от специфики разрабатываемого программного обеспечения. В качестве примера ниже приведен список вопросов, который можно использовать при анализе правильности программ, написанных на языке С/С++: · Контроль обращений к данным: o Все ли переменные инициализированы? o Не выходят ли индексы за границы массивов? o Присутствуют ли переменные со сходными именами? o Не превышены ли максимальные размеры массивов и строк? o Соответствуют ли типы записываемых и читаемых значений? o Используются ли файлы? Если да, то при вводе из файла проверяется ли завершение файла? Правильно ли завершается работа с файлом? o Используются ли нетипизированные переменные, динамическая память? Если да, то соответствуют ли типы переменных при «наложении» формата? · Контроль вычислений o Правильно ли записаны выражения (порядок следования операторов)? o Корректно ли выполнены вычисления над неарифметическими переменными? o Корректно ли выполнены вычисления с переменными различных типов (в том числе с использованием целочисленной арифметики)? o Возможно ли переполнение разрядной сетки или ситуация машинного нуля? o Соответствуют ли вычисления заданным требованиям точности? o Присутствуют ли сравнения переменных различных типов? · Контроль передачи управления: o Будут ли корректно завершены циклы? o Будет ли корректно завершена программа? o Существуют ли поисковые циклы? Корректно ли отрабатываются ситуации «элемент найден» и «элемент не найден»? o Существуют ли циклы, которые не будут выполняться из-за нарушения условия входа? Корректно ли продолжатся вычисления? · Контроль межмодульных интерфейсов: o Соответствуют ли списки параметров и аргументов по порядку, типу, единицам измерения? o Не изменяет ли подпрограмма аргументов, которые не должны изменяться? o Не происходит ли нарушения области действия глобальных и локальных переменных с одинаковыми именами? Кроме непосредственного обнаружения ошибок, результаты инспекции позволяют программисту увидеть другие сделанные им ошибки, получить возможность оценить свой стиль программирования, выбор алгоритмов и методов тестирования. Инспекция является способом раннего выявления частей программы, с большей вероятностью содержащих ошибки, что позволяет при тестировании уделить внимание именно этим частям. Современные системы управления версиями (Version Control System, VCS) и другие специальные инструментальные средства, дают возможность проведения совместной инспекции кода, без необходимости личного присутствия всех людей в одной комнате. VCS позволяет хранить несколько версий одного и того же файла, при необходимости возвращаться к более ранним версиям, определять, кто и когда сделал то или иное изменение, и многое другое. Такие системы наиболее широко используются при разработке программного обеспечения для хранения исходных кодов разрабатываемой программы. Большая часть из них, оптимизирована для работы с текстовыми файлами, но существуют и специальные системы для графических, звуковых и прочих файлов. Популярные системы управления версиями: · Subversion (http://ru.wikipedia.org/wiki/Subversion); · Mercurial (http://ru.wikipedia.org/wiki/Mercurial). · Git (http://ru.wikipedia.org/wiki/Git); Активно развиваются программы для автоматизированной инспекции кода, которые сканируют его на предмет обнаружения наиболее известных ошибок и уязвимостей. Они могут распознать большую часть известных типовых ошибок/недочетов при программировании. Однако, в вопросах, связанных с архитектурой и спецификой проекта, помочь они вряд ли смогут. Примеры программ автоматизированной инспекции кода: · Visual Studio Code Analysis. · FxCop; Доказано, что 65-80% ошибок проектирования и кодирования выявляется при интенсивном применении технологий статического тестирования еще на этапе разработки проекта. Следовательно, статическое тестирование должно обязательно применяться на проекте, поскольку его применение может значительно повысить качество и надежность программного продукта.
Тема 3.2. Динамическое тестированиеКогда программный продукт приобретает уже какие-то конкретные очертания, можно начинать использовать динамическое тестирование. В отличии от статического тестирования, здесь уже подразумевается выполнение исходного кода. Существующие на сегодня методы тестирования ПО не позволяют однозначно и полностью выявить все дефекты и установить корректность функционирования анализируемой программы, поэтому методы тестирования действуют в рамках формального процесса проверки исследуемого или разрабатываемого ПО. Такой процесс формальной проверки, может доказать, что дефекты отсутствуют с точки зрения используемого метода. Однако, нет никакой возможности точно установить или гарантировать отсутствие дефектов в программном продукте с учётом человеческого фактора, присутствующего на всех этапах жизненного цикла ПО. Существует множество подходов к решению задачи тестирования ПО, но эффективное тестирование сложных программных продуктов – это процесс в высшей степени творческий, не сводящийся к следованию строгим и чётким процедурам или созданию таковых. Существует несколько признаков, по которым принято производить классификацию видов/типов/стратегий тестирования. Обычно выделяют следующие: · По знанию системы: o «Чёрный Ящик» (Black Box); o «Белый Ящик» (White Box). · По степени изолированности компонентов: o Модульное (Unit); o Интеграционное (Integration); o Системное (System/End-to-End). · По объекту тестирования: o Функциональное (Functional); o Нефункциональное (Non-Functional). · По степени автоматизации: o Ручное (manual); o Автоматизированное (automated). · По признаку позитивности сценариев: o Позитивное (positive); o Негативное (negative). Указанная выше классификация неполная, ее можно считать самой общей, фактически верхнего уровня, но существуют и другие. В каких-либо статьях и учебниках, перечень видов тестирования и их классификация может быть другой. Важно понимать, что при изложении какого-либо материала, иногда бывает логичнее сгруппировать виды тестирования особым образом. Однако, это не означает что в теории тестирования программного обеспечения царит полный беспорядок (хотя в некоторой мере он там присутствует) и каждый делает так как хочет. Просто есть общие классификации, а есть специфичные для конкретной области. Черный и Белый ящик. Тестирование «Чёрного ящика» – это стратегия тестирования, проводимая без знания внутренних механизмов работы продукта. Производится на основании внешних проявлений работы продукта. Внутренняя структура программы при этом скрыта, известны лишь ее функции. Исследуется работа каждой функции на всей области определения. В терминах программного обеспечения под тестированием «черного ящика» чаще всего подразумевают тестирование через интерфейс пользователя, не имея доступа к исходному коду продукта. Существует еще стратегия тестирования «Белого ящика». В этом случае тестировщик должен обладать знаниями о внутренних механизмах работы продукта и иметь доступ к исходному коду приложения. Внутренняя структура программы при этом известна. Исследуются внутренние элементы программы и связи между ними. Модульное тестирование, а котором речь пойдет дальше, проводимое, как правило, разработчиками продукта, является примером такого тестирования. Модульное тестированиеМодульное тестирование (Юнит-тестирование, Unit-testing) – процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы. Основная цель, это изоляция отдельных частей программы и демонстрация, что по отдельности они работоспособны. Задача состоит в том, чтобы писать небольшие тесты для каждой нетривиальной функции или метода. Это позволяет достаточно быстро проверить, не привело ли очередное изменение кода к регрессии, то есть к появлению ошибок в уже оттестированных местах программы, а также облегчает обнаружение и устранение таких ошибок. Данный тип тестирования обычно выполняется программистами. Подробнее: http://software-testing.ru/library/testing/general-testing/77-2008-09-29-07-30-13. Модульное тестирование позволяет программистам проводить рефакторинг (http://ru.wikipedia.org/wiki/Рефакторинг), будучи уверенными, что модуль по-прежнему работает корректно (регрессионное тестирование). Это поощряет программистов к изменениям кода, поскольку достаточно легко проверить, что код работает и после изменений. С модульным тестированием так же часто связывают и такую характеристику как Покрытие Кода (Code Coverage, http://ru.wikipedia.org/wiki/Покрытие_кода). Она показывает какой процент исходного кода участвует (покрывается) при выполнении юнит-тестов. Обычно используется для того, чтобы получить набор тестов для регрессионного тестирования, тщательно проверяющих весь исходный код. Количество тестов, их состав и этап разработки, на котором они будут создаваться, зависит от выбранного подхода: · Структурное Тестирование, использует концепцию максимально полного тестирования всех маршрутов программ. То есть сначала пишется алгоритм, а потом для всех его ветвлений пишутся тесты. · Разработка через Тестирование (Test-Driven Development, TDD): сначала пишется тест, покрывающий желаемое изменение, затем пишется код, который позволит пройти этот тест. Основывается на повторении очень коротких циклов разработки. · Разработка через Поведение (Behavior-Driven Development, BDD): эволюционное ответвление TDD, где произведен переход от мышления в формате тестов к мышлению в формате поведения. Структурное тестирование называют также тестированием по «маршрутам», так как в этом случае тестовые наборы формируют путем анализа маршрутов, предусмотренных алгоритмом. Под маршрутами при этом понимают последовательности операторов программы, которые выполняются при конкретном варианте исходных данных. В основе структурного тестирования лежит концепция максимально полного тестирования всех маршрутов программы. Так, если алгоритм программы включает ветвление, то при одном наборе исходных данных может быть выполнена последовательность операторов, реализующая действия, которые предусматривает одна ветвь, а при втором - другая. Соответственно, для программы будут существовать маршруты, различающиеся выбранным при ветвлении вариантом. Считают, что программа проверена полностью, если с помощью тестов удается осуществить выполнение программы по всем возможным маршрутам передач управления. Однако нетрудно видеть, что даже в программе среднего уровня сложности число неповторяющихся маршрутов может быть очень велико, и, следовательно, полное или исчерпывающее тестирование маршрутов, как правило, невозможно. Разработка через Тестирование является уже более требовательным к программисту подходом, так как ему потребуется переосмысливать свое представление о модульных тестах. Основная идея состоит в том, что сначала пишется тест, покрывающий желаемое изменение или логику, затем пишется код, который позволит пройти тест. Тесты должны писаться для тестируемой функциональности. Считается, что это имеет два преимущества. Это помогает убедиться, что приложение пригодно для тестирования, поскольку разработчику придется с самого начала обдумать то, как приложение будет тестироваться. Это также способствует тому, что тестами будет покрыта вся функциональность. Когда функциональность пишется до тестов, разработчики и организации склонны переходить к реализации следующей функциональности, не протестировав существующую. Подробнее: http://ru.wikipedia.org/wiki/Разработка_через_тестирование. Разработка через Поведение является эволюционным ответвлением от TDD. Основное отличие этого подхода, от своего прародителя, заключается в том, что тестируется не метод или изменение, а ожидаемое поведение. Если первое сильно зависит от конкретной реализации, то второе фиксируется в спецификации и основывается на требуемой функциональности. Подробнее, в переводе статьи Дэна Норта (Dan North): http://agilerussia.ru/practices/introducing-bdd/. Интеграционное тестированиеПри тестировании модулей программного обеспечения, так же, как при проектировании и кодировании возможно применение как восходящего, так и нисходящего подходов. Восходящее тестирование. Восходящий подход предполагает, что каждый модуль тестируют отдельно на соответствие имеющимся спецификациям на него, затем собирают оттестированные модули в модули более высокой степени интеграции и тестируют их. При этом проверяют межмодульные интерфейсы, используемые для подключения модулей более низкого уровня иерархии. И так далее, пока не будет собран весь программный продукт. Нисходящее тестирование. Нисходящее тестирование органически связано с нисходящим проектированием и разработкой: как только проектирование какого-либо модуля заканчивается, его кодируют и передают на тестирование. В этом случае автономно тестируется только основной модуль. При его тестировании все вызываемые им модули заменяют модулями, которые в той или иной степени имитируют поведение вызываемых модулей. Такие модули принято называть «заглушками». В отличие от тестирующих программ заглушки очень просты, например, они могут просто фиксировать, что им передано управление. Часто заглушки просто возвращают какие-либо фиксированные данные. Как только тестирование основного модуля завершено, к нему подключают модули, непосредственно им вызываемые, и необходимые заглушки, а затем проводят их совместное тестирование. Далее последовательно подключают следующие модули, пока не будет собрана вся система. Основной недостаток нисходящего тестирования – отсутствие автономного тестирования модулей. Поскольку модуль получает данные не непосредственно, а через вызывающий модуль, то гораздо сложнее обеспечить его «достаточное» тестирование. Основным достоинством данного метода является ранняя проверка основных решений и качественное многократное тестирование сопряжения модулей в контексте программного обеспечения. При нисходящем тестировании есть возможность согласования с заказчиком внешнего вида (интерфейса) программного обеспечения. Комбинированный подход. Чаще всего применяют комбинированный подход: модули верхних уровней тестируют нисходящим способом, а модули нижних уровней – восходящим. Этот способ позволяет с одной стороны начать с тестирования интерфейса, с другой – обеспечивает качественное автономное тестирование модулей низших уровней. Для автоматизации интеграционного тестирования применяются Системы Непрерывной Интеграции (Continuous Integration System, CIS). Принцип действия таких систем состоит в следующем: · CIS производит мониторинг Системы Управления Версиями (CVS); · При изменении исходных кодов производится обновление локального хранилища; · Выполняются необходимые проверки и модульные тесты; · Исходные коды компилируются в готовые выполняемые модули; · Выполняются тесты интеграционного уровня; · Генерируется отчет о тестировании. Таким образом, автоматические интеграционные тесты выполняются сразу же после внесения изменений, что позволяет обнаруживать и устранять ошибки в короткие сроки. На крупных проектах, с большим количеством взаимосвязанных модулей и тестов, использование CIS практически обязательно. Без этого, риск возникновения регрессионных ошибок увеличивается многократно. Системное тестированиеСистемное тестирование ПО – это тестирование, выполняемое на полной, интегрированной системе, с целью проверки соответствия системы исходным требованиям. Системное тестирование относится к методам тестирования чёрного ящика, и, тем самым, не требует знаний о внутреннем устройстве системы. Здесь уже основная роль при тестировании отводится не разработчику, а специалисту в этой области – инженеру по качеству (quality engineer), или тестировщику. Задачей специалиста по тестированию является обнаружение максимального количества несоответствий тестируемой системы и требований к ней. Для выполнения этой задачи специалист по тестированию формирует тесты, используя различные подходы, о которых будет подробнее рассказано дальше, обеспечивая всестороннее тестирование. Каждое отклонение от требований, или ошибку, обязательно документируют, заполняя специальный протокол. Указываются: время тестирования, условия в которых оно проводилось (среда, конфигурация), подробное описание всего что происходило и шагов для воспроизведения данного отклонения или ошибки. Если программист исправляет ошибку, то тестирование повторяют сначала, так как при исправлении ошибки программист может внести в программу новые ошибки. Такое тестирование называют «регрессионным».
Тема 3.3. Функциональное тестированиеФункциональное тестирование – это тестирование ПО в целях проверки реализуемости функциональных требований, то есть способности ПО в определённых условиях решать задачи, нужные пользователям. Функциональные требования определяют, что именно делает ПО, какие задачи оно решает. Существуют различные методики и типы функционального тестирования. К самым используемым и эффективным можно отнести следующую классификацию: По времени проведения: · Нерегламентируемое (Ad-Hoc); · Исследовательское (Exploratory); · Дымовое, Критического Пути, Расширенное (Smoke, Critical Path, Extended). · Регрессионное (Regression); Подходы: · Доменное (Domain); · Сценарное (Scenario Based, Soap Opera); · Основанное на Рисках (Risk Based); Нерегламентируемое и Исследовательское тестированиеНерегламентируемое тестирование (Ad Hoc testing) – это поиск ошибки экспромтом, процесс импровизации. По определению, любой может заниматься этим видом тестирования. Исследовательского тестирования (Exploratory testing), является своего рода вдумчивым нерегламентируемым тестированием, это разработка и выполнения тестов в одно и то же время. Исследовательские тесты, в отличие от сценарных тестов, не определены заранее и не выполняются в точном соответствии с планом. Звучит это просто, но на практике все весьма туманно. Это происходит из-за того, что «определенный» не означает что мы жестко фиксируем все и вся. Даже в том случае, если тщательно определены все тестовые сценарии, то работа с большим количеством интересных деталей (например, как быстро печатать на клавиатуре, или какие виды поведения программы признать ошибочными) остаются на усмотрение тестировщика. Хороший исследовательский тестировщик будет записывать идеи тестов и использовать их в последующих циклах испытаний. Такие заметки иногда очень похожи на сценарии тестирования, даже если они таковыми не являются. Если каждый следующий тест, который мы выполняем, выбирается по результатам предыдущего теста, это означает, что мы используем исследовательское тестирование. Мы начинаем заниматься поисками и исследованиями, когда мы не можем сказать, какие тесты должны быть выполнены, или, когда мы еще не имели возможности эти тесты создать, то есть мысль об их написании даже не приходила нам в голову. Если мы идем по сценариям, и на свет выплывает новая информация, которая предлагает нам лучшую стратегию тестирования, мы можем перейти к поисковому режиму (как и в случае обнаружения новой ошибки, которая требует подробного рассмотрения). Наиболее обсуждаемыми темами в управлении эффективным исследовательским циклом тестирования являются тестировщик, стратегии тестирования и отчетность. Сценарный подход к тестированию является попыткой механизировать процесс тестирования, когда берется идея из головы и излагается на бумаге. Подобный способ тестирования очень полезен. Но тестировщики, использующие исследовательский подход, придерживаются мнения, что запись тестовых сценариев и следование им «отупляет» тестировщика, мешая ему быстро находить ключевые проблемы. Чем более интеллектуальным мы можем сделать тестирование, тем больше шансов у нас будет, что мы протестируем приложение правильно и успеем сделать это вовремя. В этом и заключается мощь исследовательского тестирования: богатство этого процесса ограничивается только широтой и глубиной нашей фантазии, а также нашим пониманием природы тестируемого приложения. Исследовательское тестирование особенно полезно в сложных ситуациях тестирования, когда мало что известно о продукте, или как часть подготовки набора сценариев тестов. Основное правило заключается в следующем: исследовательское тестирование используется в тех случаях, когда выполнение следующего теста неочевидно, или, когда вы хотите выйти за рамки очевидного. Подробнее: http://habrahabr.ru/post/148479/. Дымовое, Критического Пути и Расширенное тестированиеДымовое тестирование (Smoke testing) означает минимальный набор тестов на явные ошибки. Дымовой тест может производиться как самим программистом, так и тестировщиком. Если программа не проходит минимальный набор тестов, то ее не имеет смысла отдавать на более глубокое тестирование. Подробнее: http://ru.wikipedia.org/wiki/Smoke_test. Примеры: · Ошибки открытия Web-страниц, актуально для Web-приложений: при развертывании приложения, могли неправильно настроить конфигурацию. · Ошибка инсталляции: если программный продукт не устанавливается, его тестирование, скорее всего, окажется невозможным. · Ошибки при соединении с базой данных, актуально для архитектуры клиент-сервер. Если Дымовое тестирования пройдено успешно, то можно переходить к тестированию Критического Пути (Critical Path testing). Это уровень тестирования, во время которого проверяется основная функциональность программного продукта, критичная для конечного пользователя, при стандартном его использовании. В рамках данного тестирования, как правило, проверяется большинство требований, предъявляемых к программному продукту. Расширенный тест (Extended test) – это углубленный тест, при котором проверяется вся функциональность программного продукта. Используются сложные, логически запутанные сценарии, совершаются действия, которые конечный пользователь будет совершать редко. Такие тесты выполняются потому, что программа должна корректно реагировать на любые, даже самые случайные действия пользователя и работать надёжно в любой ситуации. Конечно, не для всех программ можно выполнить подобные тесты, особенно, если нет времени, поскольку степень надёжности определяется спецификой области, целей и задач, поставленных перед продуктом. Например, программы медицинской, финансовой, военной, космической направленности должны быть максимально надёжными. А программы, предназначенные для домашнего использования, могут и не быть столь надёжными, и соответственно нет смысла тратить слишком много времени на разработку и прогон изощрённых тестовых сценариев. Регрессионное тестированиеРегрессионное тестирование (Regression testing) – собирательное название для всех видов тестирования программного обеспечения, направленных на обнаружение ошибок в уже протестированных участках исходного кода. Такие ошибки – когда после внесения изменений в программу перестает работать то, что должно было продолжать работать, – называют регрессионными ошибками. Регрессионное тестирование может включать в себя: · Проверку исправления вновь найденного дефекта (new bug-fix); · Проверку, что исправленный ранее и верифицированный дефект не воспроизводится в системе снова (old bug-fix); · Проверку, что не нарушилась работоспособность работающей ранее функциональности, если ее код мог быть затронут при исправлении некоторых дефектов в другой функциональности (side-effect). Обычно используемые методы регрессионного тестирования включают повторные прогоны предыдущих тестов, а также проверки, не попали ли регрессионные ошибки в очередную версию в результате слияния кода. Из опыта разработки ПО известно, что повторное появление одних и тех же ошибок — случай достаточно частый. Иногда это происходит из-за слабой техники управления версиями или по причине человеческой ошибки при работе с системой управления версиями. Но настолько же часто решение проблемы бывает «недолго живущим»: после следующего изменения в программе решение перестаёт работать. И наконец, при переписывании какой-либо части кода часто всплывают те же ошибки, что были в предыдущей реализации. Подробнее: http://ru.wikipedia.org/wiki/Регрессионное_тестирование. Доменное тестированиеЕсли возникает необходимость протестировать поведение системы на различных входных данных, то используется Доменное тестирование (Domain testing) – методика, основанная на классах эквивалентности. В этом случае программа рассматривается как «черный ящик», и целью тестирования является выяснение обстоятельств, в которых поведение программы не соответствует спецификации. Основные методы: · Эквивалентное Разбиение (Equivalence Partitioning); · Анализ Граничных Значений (Boundary Value Analysis); · Предположение об Ошибке (Error Guessing); · Метод Всех Пар (All Pairs); Эквивалентное Разбиение. Область всех возможных наборов, входных данных программы по каждому параметру разбивают на конечное число групп – классы эквивалентности. Наборы данных такого класса объединяют по принципу обнаружения одних и тех же ошибок: если набор какого-либо класса обнаруживает некоторую ошибку, то предполагается, что все другие тесты этого класса эквивалентности тоже обнаружат эту ошибку и наоборот. Разработку тестов методом эквивалентного разбиения осуществляют в два этапа: на первом выделяют классы эквивалентности, а на втором – формируют тесты. Выделение классов эквивалентности является эвристическим процессом, однако целесообразным считают выделять в отдельные классы эквивалентности наборы, содержащие допустимые и недопустимые значения некоторого параметра. При этом существует ряд правил: · если некоторый параметр х может принимать значения в интервале [1, 999], то выделяют один правильный класс: 1 <= х <=999 и два неправильных: х < 1 и х > 999; · если входное условие определяет диапазон значений порядкового типа, например, «в автомобиле могут ехать от одного до шести человек», то определяется один правильный класс эквивалентности и два неправильных: ни одного и более шести человек; · если входное условие описывает множество входных значений и есть основания полагать, что каждое значение программист трактует особо, например, «типы графических файлов: bmp, jpeg, gif», то определяют правильный класс эквивалентности для каждого значения и один неправильный класс, например, txt; · если входное условие описывает ситуацию «должно быть», например,«первым символом идентификатора должна быть буква», то определяется один правильный класс эквивалентности (первый символ — буква) и один неправильный (первый символ - не буква); · если есть основание считать, что различные элементы класса эквивалентности трактуются программой неодинаково, то данный класс разбивается на меньшие классы эквивалентности. Анализ Граничных Значений. Граничные Значения – это значения на границах классов эквивалентности входных значений или около них. Анализ показывает, что в этих местах резко увеличивается возможность обнаружения ошибок. Например, если в программе анализа вида треугольника было записано А + В >= С вместо А + В > С, то задание граничных значений приведет к ошибке: линия будет отнесена к одному из видов треугольника. Анализ причинно-следственных связей. Анализ причинно-следственных связей позволяет системно выбирать высокорезультативные тесты. Метод использует алгебру логики и оперирует понятиями «причина» и «следствие». Причиной к данном случае называют отдельное входное условие или класс эквивалентности. Следствием - выходное условие или преобразование системы. Идея метода заключается в отнесении всех следствий к причинам, т. е. в уточнении причинно-следственных связей. Данный метод дает полезный побочный эффект, позволяя обнаруживать неполноту и неоднозначность исходных спецификаций. Построение тестов осуществляют в несколько этапов. Сначала, поскольку таблицы причинно-следственных связей при применении метода к большим спецификациям становятся громоздкими, спецификации разбивают на «рабочие» участки, стараясь по возможности выделять в отдельные таблицы независимые группы причинно-следственных связей. Затем в спецификации определяют множество причин и следствий. Далее на основе анализа семантического (смыслового) содержания спецификации строят таблицу истинности, в которой каждой возможной комбинации причин ставится в соответствие следствие. При этом целесообразно истину обозначать «1», ложь - «0», а для обозначения безразличных состояний условий применять обозначение «X», которое предполагает произвольное значение условия (0 или 1). Таблицу сопровождают примечаниями, задающими ограничения и описывающими комбинации причин и/или следствий, которые являются невозможными из-за синтаксических или внешних ограничений. При необходимости аналогично строится таблица истинности для класса эквивалентности. И, наконец, каждую строку таблицы преобразуют в тест. При этом рекомендуется по возможности совмещать тесты из независимых таблиц. Данный метод позволяет строить высокорезультативные тесты и обнаруживать неполноту и неоднозначность исходных спецификаций. Его недостатком является неадекватное исследование граничных значений. Предположение об ошибке. Часто программист с большим опытом находит ошибки, «не применяя никаких методов». На самом деле он подсознательно использует метод «предположение об ошибке». Процедура метода предположения об ошибке в значительной степени основана на интуиции. Основная его идея заключается в том, чтобы перечислить в некотором списке возможные ошибки или ситуации, в которых они могут появиться, а затем на основе этого списка составить тесты. Другими словами, требуется перечислить те особые случаи, которые могут быть не учтены при проектировании. Метод Всех Пар. Основан на довольно простой, но эффективной идее, что подавляющее большинство ошибок выявляется тестом, проверяющим один параметр, либо сочетание двух. Ошибки, причиной которых явились комбинации трех и более параметров как правило значительно менее критичны, чем пары параметров и тем более одного, не говоря уже о том что никто не мешает дополнить свое тестовое покрытие кейсами на желаемые комбинации параметров. Метод эффективен лишь на поздних этапах разработки, либо дополненный основными функциональными тестами, так как больше нацелен на проверку особых случаев (комбинаций параметров), а не базового функционала. Для того чтобы воспользоваться методом, необходимо: · Определиться с проверяемой функциональностью. Разделить функциональность на части (компоненты, сценарии), тестируя их по отдельности, поочередно; · Выявить параметры и их значения. В качестве параметров могут выступать как настройки самой программы, так и внешние факторы. Если проводится конфигурационное тестирование, то прежде чем использовать парное тестирование следует убедиться, что основной сценарий функционирует на всех операционных системах с параметрами по умолчанию. Это значительно облегчит изучение будущих ошибок, ведь при парном тестировании в одном тесте фигурирует множество параметров со значениями не по-умолчанию, каждый из которых может стать причиной сбоя. Подробнее, можно изучить данный метод и его применение, в статье на Software-Testing.Ru: http://software-testing.ru/library/testing/general-testing/1304-2011-03-29-10-48-22 и наглядном докладе с SQA Days 7: http://www.slideshare.net/VLDCORP/ss-4134169.
Основанное на Рисках тестированиеОснованное на Рисках тестирование (Risk-Based testing) – это эвристический вид тестирования, в котором используется понимание возможных рисков при работе данного программного обеспечения. Подробное описание этого вида тестирования есть в статье известного эксперта – Джеймса Баха (James Bach), на английском языке: http://nilachakra.org/documents/material/L%20-%20RiskAnalysis.pdf. Ниже представлен краткий перевод. На определенном этапе тестирования, возникает необходимость анализа рисков программного проекта (в первую очередь, внутренних рисков). При анализе, необходимо ответить на 3 вопроса о каждой независимой части продукта: · Уязвимости. Какие слабости или потенциальные проблемы содержит этот компонент? · Угрозы. Какие ситуации или входные данные могут ударить по известным уязвимостям и принести катастрофические последствия в этом компоненте? · Жертвы. Кто или какие другие части продукта пострадают при возможной ошибке в этом компоненте? Такой подход требует серьезного технического анализа, который обычно проводится тестировщиками совместно с разработчиками. Разработчик выступает в роли эксперта, знающего как устроена и функционирует система, и должен в максимально доступной форме изложить это тестировщику. Тестировщик, тем временем, когда понимает суть механизма, начинает искать уязвимости, угрозы и жертв, с помощью уточняющих вопросов разработчику: · Что будет, если конкретная функция перестанет работать? · Может ли эта функция быть вызвана в неподходящее время? · Какая проверка на ошибки происходит вот здесь? · Если отсюда сюда придут некорректные данные, что произойдет в таком случае? · Какую максимальную нагрузку может выдержать этот процесс? · От каких внешних компонентов и сервисов зависит или получает данные этот процесс? · Влияет ли выполняемый процесс на внешние компоненты и сервисы? · Какие части в этом процессе вызывают наибольшее беспокойство? Это не полный список вопросов, но он может быть являться некоторым базовым набором. Дальше, остается только слушать ответы разработчика, пытаясь определить оперирует он фактами или надеждами, прислушиваясь к сомнениям в его голосе, минутным задержкам и всем остальным признакам, которые могут указывать на то, что он как следует не прочитал требования или не до конца продумал дизайн. Все неясности и смущенность указывают на потенциальные риски, и тестировщик должны выйти со встречи с четким пониманием того, как работает функционал, который вы обсудили, какие потенциальные риски он несет, и какой будет тестовая стратегия относительно каждого из них. Существует общий набор универсальных рисков: · New – что-то принципиально новое и непривычное для продукта; · Changed – что-то, куда могут достаточно часто вноситься изменения; · Upstream Dependency – нечто, что как в домино приводит к серии ошибок во всей системе; · Downstream Dependency – нечто, что сильно зависит от ошибок в другой части системы; · Critical – компонент, ошибка которого может привезти к болезненным последствиям; · Precise – компонент, поведение которого должно идеально точно совпадать с требованиями к нему; · Strategic – все, что имеет принципиальное значение для вашего бизнеса и является базовым функционалом; · Third-party – любой компонент продукта, который будет разработан вне основной команды проекта; · Distributed – нечто распределенное в пространстве или времени, чьи элементы, тем не менее, должны работать вместе; · Buggy – нечто, что заведомо несет в себе потенциальные ошибки. После формирования списка рисков, остается решить какие компоненты будут тестироваться. Все зависит от специфики проекта и требовательности технического задания. В любом случае, тестирование всех рисков невозможно, поэтому придется тщательно ранжировать их список по степени важности. Для распределения рисков между проектными задачами, можно воспользоваться матрицей рисков/задач. Это табличка из двух колонок, в левой колонке – список рисков, в правой – список задач, при этом риски отсортированы по важности, начиная с самого критичного. На словах это звучит так: «Если мы обеспокоены риском Х, то мы должны уделить больше внимания задаче Y.
Тема 3.4. Нефункциональное тестированиеПосле завершения функционального тестирования приступают к нефункциональному тестированию, целью которого является проверка нефункциональных аспектов программного обеспечения. Эта стадия тестирования особенно важна для программных продуктов, предназначенных для продажи на рынке. Виды нефункционального тестирования классифицируются по объекту тестирования: · Производительность (Performance): o Нагрузка, Нагрузочное (Load) – проверка выполнения программы на возможность обработки большого объема данных, поступивших в течение короткого времени; o Стресс, Стрессовое (Stress); o Объем, Объемное (Volume) – проверка работоспособности программы на максимально больших объемах данных, например, объемах текстов, таблиц, большом количестве файлов и т. п.; o Cтабильность (Stability) – проверка надежности и работоспособности приложения при длительном тестировании с ожидаемым уровнем нагрузки. · Безопасность (Security) – проверка защиты, например, от несанкционированного доступа к информации; · Локализация (Localization); · Совместимость (Compatibility) – проверка преемственности версий: в тех случаях, если очередная версия системы меняет форматы данных, она должна предусматривать специальные конвекторы, обеспечивающие возможность работы с файлами, созданными предыдущей версией системы; · Удобство Использования (Usability) – анализ психологических факторов, возникающих при работе с программным обеспечением; это тестирование позволяет определить, удобен ли интерфейс, не раздражает ли цветовое или звуковое сопровождение и т. п.; На практике, обычно выполняют не все виды оценочного тестирования, так как это очень дорого и трудоемко. Как правило, для каждого типа программного обеспечения выполняют те виды тестирования, которые являются для него наиболее важными. Так базы данных обязательно тестируют на предельных объемах, а системы реального времени - на предельных нагрузках.
содержание .. 1 2 3 ..
|
|
|