Описание предполагаемых значений выходных данных или результатов должно быть необходимой частью тестового набора.
Нарушение этого принципа приводит к одной из наиболее распространенных ошибок. Ошибочные, но правдоподобные результаты могут быть признаны правильными, если результаты теста не были заранее определены. Здесь проявляется одно из свойств психологии человека: мы видим то, что хотим увидеть. Подсознательно у человека есть желание видеть корректный результат работы программы. Одним из способов борьбы с этим состоит в поощрении детального анализа выходных переменных заранее, при разработке теста. Поэтому тест должен включать описание входных данных и точное описание корректного результата, соответствующего данному входному набору.
Следует избегать тестирования программы ее автором. Процесс тестирования имеет деструктивный, а не созидательный характер, что является противоположностью созидательному процессу разработчика. Поэтому программисту трудно быстро перестроиться и поменять отношение к продукту своего труда. Опять, подсознательно, он хочет видеть свою программу корректной и может не заметить неверный результат. Перспектива создать о себе мнение как о специалисте, делающем много ошибок, не воодушевляет разработчика, и он невольно снижает требования к тщательности тестирования. В дополнение к этой проблеме следует отметить еще одну, не менее важную: программа может содержать ошибки, связанные с неверным пониманием постановки или описания задачи программистом. Тогда существует вероятность, что к тестированию программист приступит с таким же непониманием.
Программирующая организация не должна сама тестировать разработанные ей программы. Здесь можно привести те же аргументы, что и в предыдущем случае. В процессе тестирования самой организацией трудно быть объективной, поскольку тестирование может быть рассмотрено как средство уменьшения вероятности соответствия программы заданным временным и стоимостным параметрам. Экономически более целесообразно выполнение тестирования независимым подразделением.
Необходимо досконально изучать результаты применения каждого теста. В экспериментах, проверенных Майерсом [1,с.28], многие испытуемые не смогли обнаружить ошибки, хотя их признаки были явными в выходных листингах программ. Можно предположить, что значительная часть всех обнаруженных в итоге ошибок, могла быть выявлена уже на первых этапах тестирования, но они были пропущены вследствие недостаточно тщательного анализа результатов этих первых этапов.
Тесты для неправильных и непредусмотренных входных данных следует разрабатывать так же тщательно, как для правильных и предусмотренных. При тестировании имеется естественная тенденция концентрировать внимание на правильных и предусмотренных входных условиях, а неправильным и непредусмотренным входным данным не придавать значения. Множество ошибок можно обнаружить, если использовать программу новым, не предусмотренным ранее способом. Тесты, представляющие неверные и неправильные входные данные, таким образом, обладают большей обнаруживающей способностью, чем тесты, соответствующие корректным входным данным.
Необходимо проверять не только, делает ли программа то, для чего она предназначена, но и не делает ли она то, что не должна делать. Необходимо проверить тестируемую программу на нежелательные побочные эффекты. Такие эффекты могут проявиться даже при корректных наборах входных данных, например на начальных или конечных этапах выполнения. Часто эти эффекты связаны с системными особенностями функционирования программы (например, чтение или запись в файл, анализ возвращаемых значений функций API и т.д.).
Не следует уничтожать тесты, даже если программа уже не нужна. Эта проблема наиболее часто возникает при использовании интерактивных систем отладки. Тестирующий на ходу придумывает тесты и запускает программу на выполнение. При такой практике работы после применения тесты пропадают. После внесения изменений или исправления ошибок необходимо повторять тестирование и тогда приходиться заново разрабатывать тесты. Это может привести к тому, что последующее тестирование может не быть таким тщательным как первое.
Нельзя планировать тестирование в предположении, что ошибки не будут обнаружены. Такой подход характерен для руководителей проекта, использующие неверное представление тестирования, как процесса демонстрации отсутствия ошибок в программе.
Вероятность наличия необнаруженных ошибок в части программы пропорциональна числу ошибок, уже обнаруженных в этой части. Справедливость этого принципа подтверждается тем, что для ошибок свойственно располагаться в программе в виде некоторых скоплений, хотя данное явление еще требует объяснений. По данным Брукса: в 4% кода OS/360 найдено 47% ошибок. По данным Джонса: IBM опубликовало данные о том, что в 7% модулей, созданных в процессе написания базы данных IMS, было найдено 57% ошибок. Можно также утверждать, что обычно работает правило 20/80, то есть в 20% кода может содержаться до 80% ошибок. Конечно, ошибки могут группироваться в частях программы, разрабатываемых программистами низкой квалификации или в слабо спроектированных модулях (либо классах). Принцип 20/80 позволяет вести обратную связь процесс тестирования. Если в какой-нибудь части программы обнаружено больше ошибок, чем в других, то на ее тестирование должны быть направлены дополнительные усилия.
Тестирование – процесс творческий. Вероятно, для тестирования большой программы нередко требуется больший творческий потенциал, чем для ее проектирования. Подтверждением тому служит огромное число бета-тестеров, привлекаемых производителями крупных программных продуктов и бесчисленные ошибки, обнаруживаемые уже в процессе эксплуатации.
Контрольные вопросы:
1) В чем заключается экономический аспект тестирования? 2) К каким последствиям могут привести ошибки в ПО? 3) Почему процесс тестирования труден с психологической точки зрения? 4) В чем состоит парадокс отношения к тестированию? 5) Почему не следует проводить тестирование программы ее автором? Лекция 3. Ошибки в программном обеспечении
Цель : дать определение ошибки ПО, изучить природу распространенных ошибок, дать их классификацию. Определение ошибки
Теперь нам нужно разобраться в том, что такое ошибка, иначе непонятно, что мы должны обнаруживать в программе. Вопрос не такой уж тривиальный. Обычно начинающие программисты сталкиваются только с двумя типами ошибок - синтаксическими, т.е. ошибками кодирования, и с программными. Поэтому они считают так: если результат не получен, значит, в программе есть ошибки. Все это - самые простые виды ошибок. Итак, в качестве первого приближения можно сказать, что программный продукт содержит ошибку, если его поведение не соответствует техническому проекту (или будем говорить так: спецификациям). Это было бы замечательное определение, если бы не одно маленькое 'но': а где гарантия, что спецификации верны? Чаще всего оказывается, что ошибки были заложены уже в спецификациях, и бывает даже так, что программа не соответствует спецификации, однако верна. В самом общем случае мы можем сказать, что если поведение продукта не соответствует спецификациям, ошибка, скорее всего, есть. Но если соответствие есть, это не означает, что ошибки нет. Можно представить дело и так: программный продукт содержит ошибку, если его поведение не соответствует спецификациям при использовании в установленных при разработке пределах. Вот пример. Система управлением движения самолетов в некотором районе рассчитана на 200 самолетов. Неожиданно появляется 201-й. Если система 'потеряет' какой-либо самолет либо вообще 'выпадет', то это, конечно же, ошибка. Тогда, может быть, так: в программе есть ошибка, если ее поведение не соответствует документации. Но, во-первых, программы довольно часто бывают написаны лучше, чем документированы. И ошибок в документации бывает не меньше, а зачастую больше, чем в программах. Во-вторых, в документации обычно описывается 'штатная' работа. Например, в документации написано: 'нажмите кнопку ВНИМАНИЕ и введите команду'. Пользователь по запарке нажимает кнопку два раза, и система выходит из строя. Ошибка это или нет?
Разумеется, ошибка. Получается, что в любом случае, когда пользователь не получает того, что ему разумно ожидать от продукта, мы считаем, что в ней есть ошибка. Это определение вбирает в себя лучшие свойства предыдущих определений. Конечно, пользователю разумно ожидать, что при вводе корректных данных продукт не снимается и дает правильный ответ. Под словом 'корректные' подразумеваются и соответствующие спецификациям, и документации. Это означает, что программа, спецификации и документация должны соответствовать друг другу и составлять единое целое. Поэтому мы и не говорим слово 'программа', а говорим 'продукт'. Далее. Пользователю разумно ожидать, что при вводе некорректных данных система распознает их некорректность и предпринимает соответствующие действия. И, наконец, пользователю разумно ожидать, что при возникновении неожиданных ситуаций система поведет себя достойно, минимизирует неприятные последствия и, уж конечно, выживет сама. Из всего этого следует неожиданный вывод: тестирование программного продукта должно начинаться до того, как он будет написан. Это еще один парадокс программирования. В самом деле, зачем доводить ошибочные спецификации до программ, затем составлять тесты, убеждаться в том, что спецификации ошибочны, переделывать их и переделывать программы? Уж лучше начать тестирование спецификаций, когда они только составлены. Мы уже знает, что и внешние спецификации должны проверяться на соответствие с требованиями. Но этого недостаточно. Когда мы разработали алгоритмы и написали модули, не мешает проверить всю систему на небольшом примере, чтобы убедиться в том, что алгоритмы работают правильно, что опять-таки побочные эффекты не возникают. Что касается тестирования собственно системы, то тут полезна тактика 'сверху вниз', когда тестируется еще не до конца написанная система. Увы, обычно все бывает наоборот. Сначала система вся целиком пишется, потом каждый модуль тестируем и отлаживаем поодиночке. Когда мелкие ошибки с трудом выявлены, модули соединяются и вот тут-то выявляются крупные огрехи. Многие модули приходится существенно переделывать, т.е. большая часть предыдущей работы идет прахом. Пока все до единого модули не отлажены, система в целом не работает, и показать заказчику нечего, хотя все готово на 99 процентов. И вот потом показывают результаты заказчику и все приходится делать заново.
Резюмируем: 1. В программном продукте содержится ошибка, если он не делает того, что пользователю разумно от него ожидать. 2. Тестирование программного продукта не означает только проверки программы. Тестируется еще и документация - как проектная, так и 'выходная'. 3. Тестирование собственно программы следует проводить 'сверху вниз', отыскивая сначала крупные, глобальные ошибки и лишь затем переходить к 'ловле блох'.
Англоязычная терминология
Несколько слов стоит сказать об англоязычной терминологии [3, c.2]: error – ошибка, сделанная разработчиком. Это может быть опечатка, синтаксическая ошибка или неправильное понимание спецификации. fault – то, к чему приводит (или может привести) ошибка в программе. Более точно, это разница между неккоректным участком программы и ее корректной версией (например, используется операция сравнения ‘<’ вместо ‘<=’). failure – то, к чему приводит исполнение некорректной программы, или разница между работой корректной и некорректной программ.
Классификация ошибок
Можно дать несколько классификаций ошибок. Согласно одной из них ошибки можно разделить на · Синтаксические, или связанные с опечатками в тексте и неправильном использовании конструкций языка. · Алгоритмические, или связанные с неправильным решением задачи.
Синтаксические ошибки достаточно легко могут быть обнаружены транслятором языка, и их устранение не представляет больших проблем. Гораздо серьезнее дело обстоит с алгоритмическими ошибками, которые связаны с неверным выполнением программы и которые долгое время могут оставаться неуловимыми. Согласно другой классификации, ошибки можно разделить на: · Возникающие при проектировании. · Семантические или языковые.
Ошибки проектирования связаны с неверным пониманием предметной области, построением моделей объектов и схемы их взаимодействия. Чем сложнее предметная область, тем выше вероятность появления этого типа ошибок. В работе [15, c.34] описаны несколько разновидностей семантических (языковых) ошибок, распространенных при написании программ.
1. Функциональное несоответствие программы алгоритму. Такие программы означают неполную программную реализацию функций алгоритма или неверный порядок реализации функций. 2. Неправильный тип, структура или значение константы. Появление такой ошибки означает, что используемая в программе постоянная величина имеет неправильный тип, размер или указано ее неверное значение. 3. Наложение массивов или переменных. Возникает ситуация, когда обращение в программе к какой-то переменной по идентификатору приводит фактически к обращению к памяти, отведенной для другой переменной. 4. Неправильное формирование логического выражения для условного оператора. Такие ошибки возникают, когда сравниваются два разнотипных операнда, если один из них не определен, или не те операнды, которые предполагались при разработки программы., а также, когда задано ошибочное условие (отношение) между сравниваемыми операндами. 5. Ошибки ввода/вывода информации. Это, как правило, следствие неправильно сформированной порции данных, предназначенной для ввода или вывода, неправильное использование протокола и системных вызовов. 6. Неверная передача управления оператором перехода. 7. Отсутствие инициализации локальных переменных. Содержимое локальных переменных после их объявления не определено, поэтому действия с этими переменными могут быть очень опасными. 8. Ошибочное применение одних синтаксических конструкций вместо других. Возникает при замене одних элементарных синтаксических конструкций языка другими, причем при этом не нарушается синтаксис языка программирования. В результате транслятор не обнаруживает такие ошибки. 9. Неправильный результат арифметических вычислений, обусловленный спецификой процесса программирования. Представляет собой появление в результате выполнения последовательности арифметических операций числа, отличного от предполагаемого. Это может быть следствием неправильно заданной последовательности операций, пропуска отдельных операций, специфики выполнения операций над числами данного типа, потери точности вычислений. 10. Некорректное внесение изменений в программу. Вызывает вторичные, наведенные ошибки. Они появляются после того, как в некоторые части программы были внесены корректировки, но при этом не учитывалось возможное их влияние на другие части программы. 11. Неправильный тип или значение фактического параметра. При вызове из одной функции другой, вместо нужных значений фактических параметров передаются неверные значения. 12. Сдвиг на единицу при индексировании. Такая ошибка, как правило, появляется в выражениях, использующих в качестве операндов переменные с индексами. Сущность ошибки в том, что при использовании переменной с индексом (обычно на последнем шаге циклических вычислений) значение индекса на единицу больше или меньше граничного значения. В этой ситуации может произойти выход за границу массива. 13. Отсутствие анализа кодов ответа. Если при возврате управления из процедуры, вызвавшая ее процедура не анализирует код ответа, то неправильная работа вызванной подпрограммы может неопределенным образом повлиять на работу вызвавшей. 14. Неправильное употребление вложенных операторов IF. Такая ошибка возникает при построении вложенных конструкций с IF. Ошибка состоит либо в нарушении последовательности вложенных проверок истинности заданных выражений, либо в задании неправильного действия при вычислении значения логического выражения на каком-то уровне вложенной конструкции.
Эти, а также ряд других ошибок описан в приложениях I и II при построении списка вопросов для просмотров и инспекций программного кода.
Контрольные вопросы:
1) Дайте определение ошибки. 2) Где могут располагаться ошибки в программе? 3) Как классифицировать ошибки, исходя из важности их нахождения? 4) Перечислите наиболее часто встречающиеся ошибки при программировании циклических конструкций. 5) Как можно уменьшить число ошибок в программе со множеством процедур? Лекция 4. Методы тестирования
Цель : изучить основные методы тестирования, их применимость, а также слабые и сильные стороны.
Программа в общем случае представляет собой сложный объект с огромным количеством внутренних состояний, сменяющихся во времени. Этот объект может быть представлен набором входных и выходных значений, имеющих некоторую зависимость. При таком подходе программа рассматривается как черный ящик и ее внутренняя структура не принимается во внимание. Целью тестирования по принципу черного ящика является «выяснение обстоятельств, в которых поведение программы не соответствует ее спецификации» [1,с.21]. При таком подходе обнаружение всех ошибок в программе является критерием исчерпывающего входного тестирования, то есть в качестве тестовых наборов используются все возможные наборы входных данных. Такой подход очевиден, – если программа рассматривается как черный ящик, то мы должны испытать ее работу на всех возможных входных данных для получения полной уверенности в ее правильной работе. Это требование часто оказывается просто невыполнимым, поскольку наборов входных данных может быть бесконечное количество и тестирование в этом случае окажется физически невыполнимым. У рассматриваемого способа тестирования есть еще один крупный недостаток, – он не позволяет найти взаимоуничтожающихся ошибок. Стратегия «белого ящика» позволяет исследовать внутреннюю структуру программы. В этом случае данные теста получаются путем анализа логики работы программы. В методе черного ящика мы рассмотрели исчерпывающее входное тестирование, которое заключается в переборе всех возможных наборов входных данных. В методе белого ящика данному критерию должен быть сопоставлен критерий исчерпывающего тестирования маршрутов. Под маршрутом здесь понимается возможный путь выполнения программы, последовательная смена всех ее состояний. Также как и для черного ящика, здесь невозможно перебрать все маршруты, поскольку для современных программ их число астрономически велико. В следующей таблице, заимствованной из книги [13], приводятся показатели минимальной, средней и максимальной эффективности различных методов тестирования. Обратите особое внимание на последнюю строчку, описывающую общую эффективность процесса, использующего все эти методики:
Итак, мы видим, что тестирование максимально эффективно в тех случаях, когда программа проверяется не только путем запуска, но и путем чтения, статических проверок и т.п.
Проектирование тестов
Стратегии исчерпывающего тестирования, как было показано раньше, не могут быть реализованы. Если ввести ограничения на время, стоимость, то ключевым вопросом тестирования становится следующий: Какое подмножество всех возможных тестов имеет наивысшую вероятность обнаружения большинства ошибок? Изучение методологий тестирования поможет ответить на него. Наиболее неудачным следует признать подход, при котором тестирование проводится со случайными входными данными. Случайно выбранный набор тестов имеет малую вероятность быть оптимальным подмножеством полного набора тестов [1, с.54]. Поэтому усилиями многих исследователей были сформулированы следующие методологии: Черный ящик: 1) Эквивалентное разбиение. 2) Анализ граничных значений. 3) Применение функциональных диаграмм. 4) Предположение об ошибке.
Белый ящик. 1) Покрытие операторов. 2) Покрытие решений. 3) Покрытие условий.
Обычно, при проектировании реального теста используется комбинация данных методик, поскольку каждая позволяет лучше обнаруживать какие-то одни типы ошибок. Рекомендуемая процедура заключается в том, чтобы разрабатывать тесты, используя методы черного ящика, а затем – методы белого ящика.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|