Самое большое количество ошибок обнаруживается

Классификация ошибок

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

В целом сложность отладки обусловлена следующими причинами:

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

В соответствии с этапом обработки, на котором проявляются ошибки, различают:

  • синтаксические ошибки — ошибки, фиксируемые компилятором (транслятором, интерпретатором) при выполнении синтаксического и частично семантического анализа программы; 
  • логические ошибки — …; 
  • ошибки компоновки — ошибки, обнаруженные компоновщиком (редактором связей) при объединении модулей программы;
  • ошибки выполнения — ошибки, обнаруженные операционной системой, аппаратными средствами или пользователем при выполнении программы.

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

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

if (c = n) x = 0; /* в данном случае не проверятся равенство с и n, а выполняется присваивание с значения n, после чего результат операции сравнивается с нулем, если программист хотел выполнить не присваивание, а сравнение, то эта ошибка будет обнаружена только на этапе выполнения при получении результатов, отличающихся от ожидаемых.

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

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

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

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

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

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

Методы отладки программного обеспечения

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

  • ручного тестирования;
  • индукции;
  • дедукции;
  • обратного прослеживания.

Метод ручного тестирования

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

Метод индукции

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

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

Метод дедукции

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

Метод обратного прослеживания

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

Методы и средства получения дополнительной информации

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

  • отладочный вывод;
  • интегрированные средства отладки;
  • независимые отладчики.

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

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

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

Интегрированные средства отладки. Большинство современных сред программирования (Delphi, Builder C++, Visual Studio и т. д.) включают средства отладки, которые обеспечивают максимально эффективную отладку. Они позволяют:

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

Отладка с использованием независимых отладчиков. 

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

Общая методика отладки программного обеспечения

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

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

2 этап — локализация ошибки — определение конкретного фрагмента, при выполнении которого произошло отклонение от предполагаемого вычислительного процесса. Локализация может выполняться:

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

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

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

4 этап — исправление ошибки — внесение соответствующих изменений во все операторы, совместное выполнение которых привело к ошибке.

5 этап — повторное тестирование — повторение всех тестов с начала, так как при исправлении обнаруженных ошибок часто вносят в программу новые.

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

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

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

Источник:

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

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

  • операционной системы,

  • среды и языка программирования,

  • реализуемых процессов,

  • природы и специфики различных ошибок,

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

5.2.1. Классификация ошибок.

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

В целом сложность отладки обусловлена
следующими причинами:

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

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

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

  • отсутствуют четко сформулированные
    методики отладки.

В соответствии с этапом обработки, на
котором проявляются ошибки, различают
(рис. 10.1):

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

ошибки компоновки — ошибки,
обнаруженные компоновщиком (редакто­ром
связей) при объединении модулей программы;

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

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

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

if(c=n)
x=0;/*
в данном случае не
проверятся равенство с и n,
а выполняется присваивание с значения
n,
после чего результат операции сравнива­ется
с нулем, если программист хотел выполнить
не присваивание, а сравне­ние, то эта
ошибка будет обнаружена только на этапе
выполнения при полу­чении результатов,
отличающихся от ожидаемых*/

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

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

  • появление сообщения об
    ошибке, зафиксированной схемами контроля
    выполнения машинных команд, например,
    переполнении разрядной сетки, ситуации
    «деление на ноль», нарушении адресации
    и т. п.;

  • появление сообщения об ошибке,
    обнаруженной операционной систе­мой,
    например, нарушении защиты памяти,
    попытке записи на устройства,

    защищенные
    от записи, отсутствии файла с заданным
    именем и т. п.;

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

  • несовпадение полученных результатов
    с ожидаемыми.

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

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

  • неверное определение
    исходных данных,

  • логические ошибки,

  • накопление погрешностей результатов
    вычислений (рис. 10.2).

Неверное определение
исходных данных

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

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

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

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

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

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

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

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

  • опосредованного проявления ошибок;

  • возможности взаимовлияния ошибок;

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

  • отсутствия повторяемости
    проявлений некоторых ошибок от запуска
    к запуску — так называемые стохастические
    ошибки;

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

  • написания отдельных частей программы
    разными программистами.

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

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

операционной системы,

среды и языка программирования,

реализуемых процессов,

природы и специфики различных ошибок,

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

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

Вцелом сложность отладки обусловлена следующими причинами:

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

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

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

отсутствуют четко сформулированные методики отладки.

Всоответствии с этапом обработки, на котором проявляются ошибки, различают (рис. 10.1):


    синтаксические ошибки — ошибки, фиксируемые компилятором (транслятором, интерпретатором) при выполнении синтаксического и частично семантического анализа программы; ошибки компоновки — ошибки, обнаруженные компоновщиком (редактором связей) при объединении модулей программы;

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

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

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

if (c = n) x = 0; /* в данном случае не проверятся равенство с и n, а выполняется присваивание с значения n, после чего результат операции сравнивается с нулем, если программист хотел выполнить не присваивание, а сравнение, то эта ошибка будет обнаружена только на этапе выполнения при получении результатов, отличающихся от ожидаемых */ 

Ошибки компоновки. Ошибки компоновки, как следует из названия, связаны с проблемами,

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

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

• появление сообщения об ошибке, зафиксированной схемами контроля выполнения машинных команд, например, переполнении разрядной сетки, ситуации «деление на ноль», нарушении адресации и т. п.;

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

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

несовпадение полученных результатов с ожидаемыми.

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

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

неверное определение исходных данных,

логические ошибки,

накопление погрешностей результатов вычислений (рис. 10.2).

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

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

Кпоследней группе относят:

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

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

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

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

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

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

опосредованного проявления ошибок;

возможности взаимовлияния ошибок;

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

отсутствия повторяемости проявлений некоторых ошибок от запуска к запуску – так называемые стохастические ошибки;

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

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

Методы отладки программного обеспечения

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

ручного тестирования;

индукции;

дедукции;

обратного прослеживания.

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

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

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

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

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

В процессе доказательства пытаются выяснить, все ли проявления ошибки объясняет данная гипотеза, если не все, то либо гипотеза не верна, либо ошибок несколько.

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

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

Практическая работа
№ 5

Тема. Определение количества ошибок в программном продукте и числа
необходимых тестов.

Цель. Изучить процесс и организацию тестирования; критерии качества
тестирования; методы, используемые при тестировании; классификацию и оценку
ошибок.

Оборудование. ПК

Ход работы

1. Ознакомиться с теоретической частью.

2. Выполнить практическое задание.

3. Ответить на контрольные вопросы.

4. Оформить отчет.

Теоретическая часть

Тестирование процесс
выявления ошибок в программе, а
отладка
процесс их устранения. Более
многословное определение тестирования даёт стандарт IEEE
829-1983 «Standard for Software Test Documentation»: «тестирование это
процесс анализа ПО, направленный на выявление отличий между его реально
существующими и требуемыми свойствами и на оценку свойств ПО».

Отличие между
реально существующим и требуемым свойствами называется
дефектом,
или
ошибкой. Среди
программистов распространён термин
«bug»
(жучок) баг. Авторство
терминов
«bug» и «debugging»
приписывают то знаменитому
изобретателю Томасу Эдисону, в фонограф которого заползали жучки, то американке
Грейс Хоппер, которая в конце II мировой войны работала с компьютером
Mark II и вынуждена была искать и устранять из него жучков,
вызывавших замыкания и другие сбои в работе
. Итак,
ошибки есть отклонения объектов (программ, документов) от спецификаций, при
условии, что последние существуют и являются правильными, а также отклонения от
требований «здравого смысла», называемых также
общесистемными требованиями (к ним, например, относятся законы математики и логики).

Объектами
тестирования являются: исходные тексты программ; исполняемые модули (программы,
библиотеки); документация. Тестирование документации должно выявить расхождения
между содержанием документов и описанных в них программ.

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

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

Три принципа
тестирования:

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

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

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

Основные проблемы организации
тестирования программы состоят в том, что:

нередко отсутствует эталон, которому должны соответствовать
результаты тестирования;

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

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

Исправление ошибки,
внесенной на стадии анализа требований и обнаруженной на стадии окончательной
проверки, стоит в среднем в 100 раз дороже, чем исправление этой же ошибки на
стадии анализа (по другим источникам – в 500 раз дороже). Для исправления
проблем, выявленных при сопровождении продукта, программисты по статистике тратят
до 60 % времени, пытаясь понять документацию и логику программы.

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

Концепция Good
Enough Quality
вовсе не эквивалентна отказу от полноценного тестирования.
Выпуск плохо протестированного продукта из-за недостатка времени – это всегда
плохо. Практика показывает, что пользователи склонны со временем забывать даже значительные
задержки с выпуском продукта, но плохое качество выпущенного продукта
запоминается на всю жизнь.

Критерии качества
тестирования

Критерии качества
тестирования иногда называют
критериями
покрытия
(test coverage), поскольку они показывают степень охвата,
«покрытия» тех или иных аспектов продукта при тестировании. Критерии покрытия
можно разделить на две группы: критерии покрытия структуры (
structural test coverage) и критерии покрытия поведения (behavioral test coverage).

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

Структурные критерии
включают оценки полноты покрытия операторов, маршрутов и данных.

1. Полнота
покрытия операторов
. Критерий
достигает 100 %, если при тестировании были выполнены все операторы программы
(хотя бы один раз). Критерий указывает, не остались ли участки кода, в которые
управление не попадало ни разу. Если есть множество не отработавшего кода,
говорить о качественном тестировании не приходится. Существуют продукты,
которые позволяют автоматически зафиксировать и визуально показать отработавшие
операторы, например Rational Pure Coverage.

2. Полнота
покрытия маршрутов
. Можно
достигнуть 100 % полноты покрытия операторов, но многие ошибки не будут
выявлены, так как они возникают не на всех маршрутах. Маршрутом, или логическим
путём, называют конкретную последовательность выполнения операторов. За счёт
операторов ветвления и циклов в программе может существовать множество
потенциальных маршрутов. Критерий достигает 100 %, если при тестировании
отработали все возможные маршруты программы (хотя бы один раз). Полнота
покрытия маршрутов – очень сильный критерий, достигнуть его максимума в
большой программе нереально. По ряду оценок, при самом тщательном тестировании
программы достигается примерно 60%-я полнота покрытия маршрутов, а с
использованием специальных средств автоматизации тестирования можно достигнуть
90 %. Установить значение полноты покрытия маршрутов тяжело. Поэтому данный
критерий слабо пригоден на практике.

3. Полнота
покрытия данных
. Даже если
добиться 100%-й полноты покрытия маршрутов, – это недостаточно для выполнения
полного тестирования. Исследования показали, что только 35 % ошибок остаются
из-за упущенных маршрутов. Дело в том, что даже на одних и тех же маршрутах
ошибки могут возникнуть или нет, в зависимости от разных входных данных.
Критерий полноты покрытия данных достигает 100 %, если при тестировании были
проверены все возможные входные данные во всех возможных сочетаниях. Это
самый сильный критерий
, но практически недостижимый. Даже для единственного
16-разрядного параметра придётся провести 65536 тестов. Два таких параметра
потребуют уже 655362 тестов (все сочетания значений). Однако и 100%-я полнота
покрытия данных не гарантирует выявления всех ошибок, поскольку некоторые
ошибки могут зависеть от состояния среды, в которой работает программа,
например от содержимого оперативной памяти.

Методы,
используемые для тестирования

1. Инспекция кода

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

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

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

2. Многократная
разработка

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

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

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

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

3. Классы
эквивалентности и граничные условия

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

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

«Классический
критерий отбора музейного объекта […] во многом отличен от принципов
составления архива и библиотеки. Если последние претендуют на максимальную
полноту (чем более обширно собрание, тем оно ценнее), то музейные коллекции
распределяются между двумя полюсами – вещь маргинальная и вещь идеальная, образцовая.
То есть, с одной стороны, мы имеем предметы, которые, не обладая самостоятельной
ценностью, дают представление о целом классе, с другой стороны – всё уникальное,
из ряда вон выходящее. Да, набор тестов – это не библиотека и не архив, это – музей».

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

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

Проще всего
выделение классов эквивалентности можно показать на примере численных
параметров. Если параметр должен принимать значения из интервала [0;100], для
него естественным образом выделяются три класса эквивалентности: один класс
допустимых значений [0;100] и два – недопустимых: (–∞;0) и (100;+∞). При
тестировании следует проверить поведение системы при подаче значения из каждого
класса.

Часто классы
эквивалентности приходится формировать для целой совокупности параметров, если
они тесно связаны. Например, подпрограмма должна выполнять поиск подстроки s
в строке S. Очевидно, что классы должны быть сформированы с учётом
наличия/отсутствия s в S. Минимально допустимый набор классов
должен включать классы {s есть в S} и {s нет в S}.

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

В примере с числом в
интервале [0;100] следует протестировать следующие значения: {–0,00001; 0;
0,00001; 99,99999; 100; 100,00001}. В примере с поиском подстроки s в строке
S следует протестировать следующие пограничные условия:

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

Организация
тестирования

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

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

В команде
тестирования специалистов делят на тест
инженеров и тестеров. Тестинженер
(
test engineer) – наиболее
квалифицированный специалист, который владеет навыками планирования
тестирования, разработки и проведения тестов, а также понимает предметную
область. Тестер (
test
technician
, test specialist)
менее квалифицированный
специалист, который главным образом занимается выполнением тестов
.

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

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

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

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

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

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

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

воспользоваться генератором тестовых данных;

подготовить тестовые данные силами проектной
группы:

привлечь пользователей к подготовке тестовых
данных; взять данные, подготовленные какой-то другой автоматизированной
системой;

выделить тестовые данные из имеющихся файлов;

выделить тестовые данные из внешних документов.

Классификация
ошибок

Существует много
классификаций ошибок
. Наиболее простой является классификация ошибок по
серьёзности
:

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

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

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

Очевидно, что
серьёзность ошибок фактически определяет приоритетность их исправления, хотя и
не безусловно.

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

1. Ошибки
анализа.
Это самые тяжёлые по
последствиям ошибки. Примеры: не предусмотрена нужная функциональность;
дублирование функций; лишние функции
;
неверно оценены требования к
техническим средствам; неверно оценены требования к производительности или
переносимости.

2. Ошибки
проектирования.
В рамках
предложенного проекта невозможно достичь требуемой в ТЗ функциональности и
критериев качества. Самые тяжёлые ошибки проектирования – архитектурные.

3. Ошибки
документации.
Расхождения
документов с программами, отсутствие нужных сведений и нужных документов.

4. Ошибки
программной реализации.
Это
самый широкий класс ошибок. Рассмотрим важные виды ошибок программной
реализации:

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

4.2. Ошибки
вычислений.
Программа не
учитывает возникновение или накопление ошибок вычислений (погрешности,
округления, переполнения и пр.)

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

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

Решение данной
проблемы необходимо искать совместно с автором метода, если это возможно, либо
совместно с квалифицированным специалистом в данной области. Наиболее часто,
разумеется, виноват программист, однако опыт свидетельствует о том, что ошибки
в методах встречаются чаще, чем хотелось бы. Как правило, методы «не работают»
на некоторых специфических наборах данных или при некоторых математических упрощениях,
которые не следовало делать. В результате метод применим в 99 % случаев, однако
в 1 % даёт сбой.

4.4. Ошибки
взаимодействия с устройствами и программами.
Программа не учитывает возможность отсутствия,
неисправности или ошибок функционирования используемых устройств (дисков,
принтеров и т. д.) или внешних программ, с которыми устанавливается связь по
специальному или известному протоколу (DDE, OLE, FTP, HTTP и т. д.).

4.5. Ошибки
синхронизации.
Программа не
учитывает возможность ошибок, вызванных неверным порядком выполнения действий
(например, принтер ещё не выбран, а печать вызывается), отсутствием
синхронизации процессов и т. п. Типичная ситуация – повторный вызов функции или
программы во время её выполнения.

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

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

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

использование неинициализированных переменных.

Оценки ошибок

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

Фирмой IBM
установлено, что в среднем одна ранее не обнаруженная ошибка появляется в
каждых
100 операторах программы. Большинство ошибок содержится в
сопряжениях модулей и операторах ввода
вывода. Такого рода ошибки
затрагивают обычно сразу несколько модулей. После того как проведено
тестирование отдельно каждого модуля, последние объединяются в систему.
Экспериментально установлено, что в
90
%
всех новых модулей и в 15 % старых при этом необходимо вносить изменения. Приблизительно в 15 %
новых модулей и 6 % старых следует внести более 10 изменений.

Если осталось D старых
модулей и сформировано M новых, число необходимых изменений N определяется
эмпирически найденным выражением

Эксперименты
показали, что число ошибок в неоттестированных программах пропорционально E2/3,
где E – мера Холстеда, характеризующая сложность программы. Коэффициент
пропорциональности равен примерно 1/3200. В программах, прошедших стадии
тестирования и отладки, это отношение сохраняется, но коэффициент пропорциональности
уменьшается.

Различные формулы
оценки количества ошибок не учитывают вероятность внесения k новых
ошибок при исправлении n старых.

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

позволяет установить
среднее количество проверок, необходимых для обнаружения n ошибок. Здесь
β – неизвестное значение, которое может быть определено
экспериментально. Исходя из предыдущей формулы, получаем формулу

которая показывает,
какой процент P тестовых прогонов, необходимых для обнаружения всех N
ошибок, был сделан, когда в процессе этих прогонов было найдено n ошибок.

Если n=10, по
формуле получим, что половина всех ошибок может быть обнаружена в первых 22%
тестовых прогонов, необходимых для обнаружения всех ошибок. Процент прогонов
возрастает до 37, если требуется найти 7 из 10 ошибок, и до 66, если требуется
обнаружить 9 ошибок из 10, то есть третья часть времени затрачивается на
обнаружение одной последней ошибки. Если в программе имеется 100 ошибок, то 90
обнаруживается за первые 44 % тестовых прогонов.

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

Практическая часть

Задание 1.      Перечислите критерии качества тестирования.

Задание 2.      Перечислите методы инспекции кода.

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

Задание 4. Выполните оценку ошибок программы нахождения среднего
арифметического
n чисел.

Задание 5.      Результаты выполнения практического задания
запишите в отчет.

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

1.     Что такое тестирование? Что является объектами
тестирования?

2.     Изобразите и опишите информационные потоки при
тестировании.

3.     Опишите виды тестирования.

4.     Поясните понятия  «тест», «тестовые данные», 
«тестовый эксперимент».

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

Цель лекции:
ознакомиться с видами и способами
контроля и тестирования ПО, методами и
средствами отладки программ.

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

Тестирование

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

  1. предполагаемые
    результаты должны быть известны до
    тестирования;

  2. следует избегать
    тестирования программы автором;

  3. необходимо
    досконально изучать результаты каждого
    теста;

  4. необходимо
    проверять действия программы на неверных
    данных;

  5. необходимо
    проверять программу на неожиданные
    побочные эффекты на неверных данных.

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

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

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

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

Ручной
контроль

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

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

В
основе структурного
тестирования

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

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

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

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

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

Отладка

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

  1. синтаксические
    ошибки

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

  2. ошибки
    компоновки

    обнаруживаются компоновщиком (редакто­ром
    связей) при объединении модулей
    программы;

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

  1. ошибки
    определения исходных данных (ошибки
    передачи, ошибки преобразования, ошибки
    перезаписи и ошиб­ки данных);

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

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

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

    1. ручного
      тестирования

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

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

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

    4. обратного
      прослеживания

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

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

Общая
методика отладки программных продуктов,
написанных для выполнения в операционных
системах MS
DOS
и Win32:

1
этап

изучение проявления ошибки;

2
этап –
определение
локализации
ошибки;

3
этап

определение причины ошибки;

4
этап —
исправление
ошибки;

5
этап —
повторное
тестирование.

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

  1. программу наращивать
    «сверху-вниз», от интерфейса к
    обрабатываю­щим подпрограммам,
    тестируя ее по ходу добавления
    подпрограмм;

  2. выводить пользователю
    вводимые им данные для контроля и
    прове­рять их на допустимость сразу
    после ввода;

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

Дополнительную
информацию по теме можно получить в [1,
2, 4, 7, 9, 14, 15].

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Аннотация: Лекция посвящена описанию вопросов, касающихся отладки приложений и работы с ошибками.

8.1. Ошибки при создании программы

Можно выделить два типа ошибок, с которыми сталкивается программист. Во-первых — это ошибки, которые сопровождают создание программ, а во вторых — ошибки времени выполнения.

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

Приведем несколько примеров синтаксических ошибок.

  • Неправильное ключевое слово. Такая ошибка сразу будет отмечена редактором. Например, если в конструкции цикла For-Next сделать ошибку в ключевом слове For — вы увидите сообщение об ошибке как только попытаетесь перейти к написанию следующей, после начала цикла, строки.
  • Ошибка при объявлении переменной. Если вы забудете указать ключевое слово Dim при объявлении переменной — редактор выдаст сообщение об ошибке. Однако, сделает он это лишь при попытке запуска программы.

А вот — логические ошибки.

  • Неправильное использование операторов. Например, вместо знака обычного деления вы случайно использовали знак целочисленного деления.
  • Расчет какого-либо показателя по неправильной формуле. Например, если неточно расставить скобки в каком-либо выражении — это приведет к ошибке, хотя внешне все может выглядеть правильным.
  • Неправильное использование функций — как встроенных, так и пользовательских. Например, используя функцию Str для получения строкового представления числа, вы не учли, что для положительных чисел эта функция добавляет в начало строки пробел. Далее вы попытались узнать первую цифру числа, вырезав первый символ полученной строки. Естественно, никакой цифры в этом случае не получится — лишь знак пробела для положительных или «минус» для отрицательных чисел.
  • Неправильное использование переменных. Например, вы используете два вложенных цикла для обработки двумерного массива. Одна из цикловых переменных имеет имя i, вторая — j. Они довольно сильно похожи внешне, их можно случайно перепутать при указании индексов массива. К тому же, обрабатывая массив в цикле довольно легко перепутать место каждой из переменных при указании индекса массива. Использование понятных имен переменных (например — my_Age или num_Vozrast для хранения возраста и т.д.) позволяет эффективно бороться с такими ошибками.
  • Случайное использование «новых» переменных. Например, вы предложили пользователю ввести некое значение и записали его в переменную num_Inp, а использовав эту переменную в выражении, напечатали не num_Inp, а num_Ihp. Внешне они похожи, но, присмотревшись, вы можете обнаружить, что имена разные. Еще сложней искать ошибки в латинских именах переменных, в которые «вкрались» русские буквы. Разницу между my_Name и my_Namе вы не увидите, но это — разные переменные — в конце второй вместо латинской e использована русская е. Эффективно бороться с такими ошибками можно, если задать в редакторе опцию обязательного объявления переменных ( Option Explicit ). При появлении необъявленной переменной редактор даст знать об этом.
  • Неправильное использование оператора сравнения. Например, это может быть оператор, который сравнивает некие величины не так, как вы предполагали — вместо знака < вы случайно использовали > или, редактируя сравнение (скажем, скопировав похожий оператор сравнения из другого места программы для ускорения работы), поменяли местами сравниваемые переменные или выражения, не поменяв знака и т.д.

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

8.2. Ошибки при выполнении программы

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

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

Давайте, для начала, рассмотрим способы поиска ошибок в процессе создания программ

8.3. Тестирование программ и поиск ошибок

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

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

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

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

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

  • Если программа запрашивает число — введите какое-нибудь слово, очень большое число, ноль, отрицательное число, оставьте поле ввода пустым, введите дробное число. Если вы не предусмотрели никаких специальных мер по обработке ошибок, при выполнении подобных операций вы почти гарантированно встретитесь с проблемами.
  • Попытайтесь запустить программу, открыв несколько окон с документами.
  • Попытайтесь прервать работу программы, а потом снова возобновить ее. Если в вашей программе есть участки, в течение выполнения которых нельзя допускать прерывания работы программы пользователем — вам следует подумать о том, чтобы запретить прерывание работы программы на этих участках.
  • Попытайтесь использовать вашу программу в более старой версии Microsoft Office, в нерусифицированной версии. Обратите внимание, например, на различия расширений имен файлов в Office 2007 и более старых версиях, на различия в объектных моделях. В общем случае программы из более старых версий Office будут работать в Office 2007, однако если они используют какие-то специфические особенности Office — такие программы нуждаются в проверке и обновлении. В то же время, макросы для Office 2007, использующие новые объектные модели, могут не работать или работать неправильно в старых версиях.
  • Попытайтесь поработать с вашей программой на чужом ПК. Вполне возможно, что при таком эксперименте вы столкнетесь с ошибкой. Например, вы программно работаете с файлами на вашем рабочем ПК — если эти файлы отсутствуют на ПК другого пользователя или находятся в других директориях, или тех директорий, которые нужны вашей программе, нет на ПК другого пользователя — вы столкнетесь с ошибкой.
  • Во время работы программы сделайте что-нибудь необычное. Как правило, от пользователей можно ожидать любых странных на первый взгляд действий. Если вы тестируете программу для MS Word, которая правит текст или занимается автоматическим созданием текста, попробуйте во время ее работы переключаться между документами, читать документ, вносить в него правки, выделять произвольные участки текста. То же самое касается MS Excel — во время работы программы попробуйте переключаться между открытыми книгами, между листами, выделять ячейки, попробуйте запустить программу, делая различные листы активными, открыв несколько книг. Результаты такого тестирования могут быть совершенно непредсказуемыми. Проанализировав их, вы можете прити к выводу, что, например, на время выполнения программы нужно скрывать или блокировать документ, пользоваться альтернативными методами работы с документом. Яркий пример — объект Selection в MS Word, который чувствителен к смене выделения в процессе работы, и объект Range, который может работать совсем без создания выделения в тексте.
  • Если ваша программа использует файлы, находящиеся в локальной сети, отключите сеть во время работы программы. Проверьте ее реакцию. То же самое можно сделать, если ваша программа работает с принтером — проверьте ее реакцию на выключенный принтер, на принтер, в котором нет бумаги.
  • Наконец, представьте, что вы — пользователь программы и просто поработайте с ней. А еще лучше — попросите потенциального пользователя немного «пообщаться» с вашей программой. Этот способ позволяет протестировать программу в условиях, максимально приближенных к реальным.

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

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

8.4. Отладка программ в редакторе VBA

Основной метод отладки — это пошаговое исполнение программы с использованием точек останова (breakpoint).

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

Точка останова в программе

Рис.
8.1.
Точка останова в программе

Следует понимать, что строка, подсвеченная при остановке программы еще не выполнялась — редактор указывает на нее, как бы говоря «Эта строка будет выполнена следующей».

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

Установить точку останова в строку можно, выделив строку и выбрав команду Debug o Toggle Breakpoint (Отладка o Установить точку останова). Для удаления всех точек останова из программы можно воспользоваться командой Debug o Clear All Breakpoints (Отладка o Очистить точки останова).

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

Так же режим отладки можно включить, нажав во время работы программы комбинацию клавиш Ctr + Pause Break и нажав в появившемся окне кнопку Debug. Текущая строка будет выделена желтым цветом, напротив нее будет установлена желтая стрелочка. Однако такой способ обычно не позволяет точно «попасть» в то место программы, где находится предполагаемая ошибка (рис. 8.2.).

Программа, остановленная во время выполнения

Рис.
8.2.
Программа, остановленная во время выполнения

Чтобы запущенная программа останавливалась на каждой строке, можно запустить ее в режиме Step Into командой Debug o Step Into (Отладка o Пошаговое исполнение). Того же эффекта можно достичь, нажав клавишу F8 на клавиатуре.

Когда программа остановлена, вы можете выполнить следующие действия

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

    Значение переменной во всплывающем окне

    Рис.
    8.3.
    Значение переменной во всплывающем окне

  • Продолжить выполнение программы в режиме Step Into — выбрав соответствующую команду меню или нажав клавишу F8.
  • Отредактировать программу.
  • Продолжить исполнение программы в обычном режиме командой Run o Sub/User Form (Запустить o Процедуру/Форму), нажатием клавиши F5 или соответствующей кнопкой на панели инструментов
  • Остановить выполнение программы командой Run o Reset (Запустить o Перезагрузка) или кнопкой на панели инструментов
  • Воспользоваться другими средствами отладки — окнами Immediate, Locals, Watch.

Помимо режима Step Into существуют следующие режимы отладки, доступные в меню Debug.

  • Step Over (Перейти на следующую строку). Эта команда полезна при отладке программы, содержащей вызовы уже отлаженных процедур. В режиме Step Over отладчик не входит в процедуру, выполняя ее без отладки, после чего переходит на следующую строку. Например, вы выполняете программу в режиме Step Into и при очередной остановке видите, что подсвеченная строка содержит вызов процедуры, которую отлаживать не нужно. Вы выбираете команду Step Over, процедура выполняется без остановок на каждой ее строке, после чего следующая остановка происходит на строке вашей программы, которая идет за вызовом процедуры.
  • Step Out (Выполнить процедуру) — эта команда позволяет выполнить текущую процедуру (например, вызванную из кода основной программы при обычной отладке ) без остановки в каждой строке. Следующая остановка будет сделана на строке, которая следует за вызовом процедуры в основном тексте программы.
  • Run To Cursor (Выполнить до курсора) — выполняет программу до позиции, на которой установлен курсор. Аналогично установке одиночной точки останова.

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

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

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

операционной системы,

среды и языка программирования,

реализуемых процессов,

природы и специфики различных ошибок,

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

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

Вцелом сложность отладки обусловлена следующими причинами:

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

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

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

отсутствуют четко сформулированные методики отладки.

Всоответствии с этапом обработки, на котором проявляются ошибки, различают (рис. 10.1):


    синтаксические ошибки — ошибки, фиксируемые компилятором (транслятором, интерпретатором) при выполнении синтаксического и частично семантического анализа программы; ошибки компоновки — ошибки, обнаруженные компоновщиком (редактором связей) при объединении модулей программы;

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

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

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

if (c = n) x = 0; /* в данном случае не проверятся равенство с и n, а выполняется присваивание с значения n, после чего результат операции сравнивается с нулем, если программист хотел выполнить не присваивание, а сравнение, то эта ошибка будет обнаружена только на этапе выполнения при получении результатов, отличающихся от ожидаемых */ 

Ошибки компоновки. Ошибки компоновки, как следует из названия, связаны с проблемами,

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

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

• появление сообщения об ошибке, зафиксированной схемами контроля выполнения машинных команд, например, переполнении разрядной сетки, ситуации «деление на ноль», нарушении адресации и т. п.;

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

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

несовпадение полученных результатов с ожидаемыми.

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

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

неверное определение исходных данных,

логические ошибки,

накопление погрешностей результатов вычислений (рис. 10.2).

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

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

Кпоследней группе относят:

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

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

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

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

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

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

опосредованного проявления ошибок;

возможности взаимовлияния ошибок;

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

отсутствия повторяемости проявлений некоторых ошибок от запуска к запуску – так называемые стохастические ошибки;

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

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

Методы отладки программного обеспечения

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

ручного тестирования;

индукции;

дедукции;

обратного прослеживания.

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

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

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

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

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

В процессе доказательства пытаются выяснить, все ли проявления ошибки объясняет данная гипотеза, если не все, то либо гипотеза не верна, либо ошибок несколько.

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

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

Привет, Вы узнаете про виды ошибок программного обеспечения, Разберем основные ее виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое
виды ошибок программного обеспечения, принципы отладки , настоятельно рекомендую прочитать все из категории Качество и тестирование программного обеспечения. Quality Assurance..

1. Отладка программы

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

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

2. Локализация ошибок

Локализация — это нахождение места ошибки в программе.

В процессе поиска ошибки мы обычно выполняем одни и те же действия:

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

Способы обнаружения ошибки:

  • Аналитический — имея достаточное представление о структуре программы, просматриваем ее текст вручную, без прогона.
  • Экспериментальный — прогоняем программу, используя отладочную печать и средства трассировки, и анализируем результаты ее работы.

Оба способа по-своему удобны и обычно используются совместно.

3.
принципы отладки

Принципы локализации ошибок:

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

Принципы исправления ошибок еще больше похожи на законы Мерфи:

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

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

4. Методы отладки

Силовые методы

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

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

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

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

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

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

var
a, b, c: real;
begin
writeln('Программа находит значение максимального из трех введенных чисел');
write('Введите первое число '); readln(a);
write('Введите второе число '); readln(b);
write('Введите третье число '); readln(c);
if (a>b)and(a>c) then
writeln('Наибольшим оказалось первое число ',a:8:2)
else if (b>a)and(a>c) then
writeln('Наибольшим оказалось второе число ',b:8:2)
else
writeln('Наибольшим оказалось третье число ',b:8:2);
end.

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

Тестовые наборы данных должны учитывать все варианты решения, поэтому выберем следующие наборы чисел:

Данные Ожидаемый результат
a=10; b=-4; c=1 max=a=10
a=-2; b=8; c=4 max=b=8
a=90; b=0; c=90.4 max=c=90.4

В результате выполнения программы мы, однако, получим следующие результаты:
Для a=10; b=-4; c=1:

Наибольшим оказалось первое число 10.00

Для a=-2; b=8; c=4: < pre class=»list»>Наибольшим оказалось третье число 8.00Для a=90; b=0; c=90.4:

Наибольшим оказалось третье число 0.00

Вывод во втором и третьем случаях явно неверен. Будем разбираться.

1. Трассировка и промежуточная наблюдение за переменными

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

  • — вывод a, b, c после ввода (проверяем, правильно ли получили данные)
  • — вывод значения каждого из условий (проверяем, правильно ли записали условия)

Листинг программы существенно увеличился и стал вот таким:

var
a, b, c: real;
begin
writeln(‘Программа находит значение максимального из трех введенных чисел’);
write(‘Введите первое число ‘); readln(a);
writeln(‘Вы ввели число ‘,a:8:2); {отл.печать}
write(‘Введите второе число ‘); readln(b);
writeln(‘Вы ввели число ‘,b:8:2); {отл.печать}
write(‘Введите третье число ‘); readln(c);
writeln(‘Вы ввели число ‘,c:8:2); {отл.печать}
writeln(‘a>b=’,a>b,’, a>c=’,a>c,’, (a>b)and(a>c)=’,(a>b)and(a>c)); {отл.печать}
if (a>b)and(a>c) then
writeln(‘Наибольшим оказалось первое число ‘,a:8:2)
else begin
writeln(‘b>a=’,b>a,’, b>c=’,b>c,’, (b>a)and(b>c)=’,(b>a)and(b>c)); {отл.печать}
if (b>a)and(a>c) then
writeln(‘Наибольшим оказалось второе число ‘,b:8:2)
else
writeln(‘Наибольшим оказалось третье число ‘,b:8:2);
end;
end.

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

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

Вывод для второго случая получается следующим:

Программа находит значение максимального из трех введенных чисел
Введите первое число -2
Вы ввели число -2.00
Введите второе число 8
Вы ввели число 8.00
Введите третье число 4
Вы ввели число 4.00
a>b=FALSE, a>c=FALSE, (a>b)and(a>c)=FALSE
b>a=TRUE, b>c=TRUE, (b>a)and(b>c)=TRUE
Наибольшим оказалось третье число 8.00

Со вводом все в порядке . Об этом говорит сайт https://intellect.icu . Впрочем, в этом сомнений и так было немного. А вот что касается второй группы операторов печати, то картина вышла интересная: в результате выводится верное число (8.00), но неправильное слово («третье», а не «второе»).

Вероятно, проблемы в выводе результатов. Тщательно проверяем текст и обнаруживаем, что действительно в последнем случае выводится не c, а b. Однако к решению текущей проблемы это не относится: исправив ошибку, мы получаем для чисел -2.0, 8.0, 4.0 следующий результат.

Наибольшим оказалось третье число 4.00

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

2. Метод индукции

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

Просматриваем все, относящееся к переменным b и с. Со вводом никаких проблем не замечено, а что касается вывода — то мы быстро натыкаемся на замену b на с. Исправляем.

Как видно, невыявленные ошибки в программе остаются. Просматриваем расчетный блок: все, что относится к максимальному b (максимум с получается «в противном случае»), и обнаруживаем пресловутую проблему «a>c» вместо «b>c». Программа отлажена.

3. Метод дедукции

Неверные результаты в нашем случае могут получиться из-за ошибки в:

  • — вводе данных;
  • — расчетном блоке;
  • — собственно выводе.

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

4. Обратное движение по алгоритму

Зная, что ошибка возникает при выводе результатов, рассматриваем код, начиная с операторов вывода. Сразу же находим лишнюю b в операторе writeln.

Далее, смотрим по конкретной ветке условного оператора, откуда взялся результат. Для значений -2.0, 8.0, 4.0 расчет идет по ветке с условием if (b>a)and(a>c) then… где мы тут же обнаруживаем искомую ошибку.

5. Тестирование

В нашей задаче для самого полного набора данных нужно выбрать такие переменные, что
a > b > c
a > c > b
b > a > c
b > c > a
c > a > b
c > b > a

Анализируя получившиеся в каждом из этих случаев результаты, мы приходим к тому, что проблемы возникают при b>c>a и с — максимальном. Зная эти подробности, мы можем заострить внимание на конкретных участках программы.

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

5. Средства отладки

Помимо методик, хорошо бы иметь представление о средствах, которые помогают нам выявлять ошибки. Это:

1) Аварийная печать — вывод сообщений о ненормальном завершении отдельных блоков и всей программы в целом.

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

3) Непосредственное слежение:

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

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

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

Виды ошибок и основные принципы отладки программного обеспеченияРис Пример отладки приложения

6. Классификация ошибок

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

Виды ошибок и основные принципы отладки программного обеспечения

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

  • — ошибки обращения к данным,
  • — ошибки описания данных,
  • — ошибки вычислений,
  • — ошибки при сравнении,
  • — ошибки в передаче управления,
  • — ошибки ввода-вывода,
  • — ошибки интерфейса,
  • и т д

Виды ошибок и основные принципы отладки программного обеспечения

Классификация ошибок по этапу обработки программы

Виды ошибок и основные принципы отладки программного обеспечения

рис Классификация ошибок этапа выполнения по возможным причинам

Синтаксические ошибки

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

Примеры синтаксических ошибок :

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

Ошибки, которые не обнаруживает транслятор

В случае правильного написания операторов в программе может присутствовать большое количество ошибок, которые транслятор не может обнаружить. Рассмотрим примеры таких ошибок:

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

Ошибки в циклах: неправильно указано начало цикла; неправильно указаны условия окончания цикла; неправильно указано количество повторений цикла; использование бесконечного цикла.

Ошибки ввода-вывода; ошибки при работе с данными: неправильно задан тип данных; организовано считывание меньшего или большего объема данных, чем нужно; неправильно отредактированы данные.

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

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

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

ошибки в архитектуре приложения пприводящие к увеличени технического долга

Методы (пути) снижение ошибок в программировании

  • использование тестиования
  • использование более простых решений
  • использование систем с наименьшим числом составлящих
  • использование ранее использованных и проверенных компонентов
  • использование более квалифицрованных специалистов

7. Советы отладчику

1) Проверяйте тщательнее: ошибка скорее всего находится не в том месте, в котором кажется.

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

3) Тщательнее следить за объявлениями констант, типов и переменных, входными данными.

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

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

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

7) Ошибка, скорее всего окажется вашей и будет находиться в тексте программы. Гораздо реже она оказывается:

  • в компиляторе,
  • операционной системе,
  • аппаратной части,
  • электропроводке в здании и т.д.

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

8) Убедитесь, что исходный текст программы соответствует скомпилированному объектному коду (текст может быть изменен, а запускаемый модуль, который вы тестируете — скомпилирован еще из старого варианта).

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

10) Старайтесь не жалеть времени, чтобы уясненить причину ошибки. Это поможет вам:
исправить программу,
обнаружить другие ошибки того же типа,
не делать их в дальнейшем.

11) Если вы уже знаете симптомы ошибки, иногда полезно не исправлять ее сразу, а на фоне известного поведения программы поискать другие ляпы.

12) Самые труднообнаруживаемые ошибки — наведенные, то есть те, что были внесены в код при исправлении других.

8. Тестирование

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

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

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

2) При прогоне программы по тестовым начальным данным, полученные результаты нужно сверить с эталонными и проанализировать разницу, если она есть.

3) При разработке тестов нужно учитывать не только правильные, но и неверные исходные данные.

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

5) Тестирование нужно планировать: заранее выбрать, что мы контролируем и как это сделать лучше. Обычно тесты планируются на этапе алгоритмизации или выбора численного метода решения. Причем, составляя тесты, мы предполагаем, что ошибки в программе есть.

6) Чем больше ошибок в коде мы уже нашли, тем больше вероятность, что мы обнаружим еще не найденные.
Хорошим называют тест, который с большой вероятностью должен обнаруживать ошибки, а удачным — тот, который их обнаружил.

9. Проектирование тестов

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

Давайте рассмотрим задачу: нужно проверить, попадает ли введенное число в заданный пользователем диапазон.

program Example;
(******************************************************
* Задача: проверить, попадает ли введенное число в *
* заданный пользователем диапазон *
******************************************************)

var
min, max, A, tmp: real;
begin
writeln(‘Программа проверяет, попадают ли введенные пользователем’);
writeln(‘значения в заданный диапазон’);
writeln;
writeln(‘Введите нижнюю границу диапазона ‘); readln(min);
writeln(‘Введите верхнюю границу диапазона ‘); readln(max);
if min>max then begin
writeln(‘Вы перепутали диапазоны, и я их поменяю’);
tmp:=min;
min:=max;
max:=tmp;
end;
repeat
writeln(‘Введите число для проверки (0 — конец работы) ‘); readln(A);
if (A>=min)and(A<=max) then
writeln(‘Число ‘,A,’ попадает в диапазон [‘,min,’..’,max,’]’)
else
writeln(‘Число ‘,A,’ не попадает в диапазон [‘,min,’..’,max,’]’);
until A=0;
writeln;
end.

Если исходить из алгоритма программы, мы должны составить следующие тесты:
ввод границ диапазона
— min< max
— min>max
ввод числа
— A < min (A<>0)
— A > max (A<>0)
— min <= A <= max (A<>0)
— A=0

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

10. Стратегии тестирования

1) Тестирование программы как «черного ящика».

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

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

«Черным ящиком» удобно тестировать небольшие подпрограммы.
2) Тестирование программы как «белого ящика».

Здесь перед составлением теста мы изучаем логику программы, ее внутреннюю структуру. Тестирование будет считаться удачным, если проверяет программу по всем направлениям. Однако, как мы уже говорили, это требует огромного количества тестов.

На практике мы, как всегда, совместно используем оба принципа.
3) Тестирование программ модульной структуры.

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

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

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

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

См. также

  • ошибки в приложениях , bugs , баг репорт , bug report ,
  • Фича
  • GIGO
  • Патч
  • тестирование
  • цикломатическая сложность
  • баг репорт
  • качество программного обеспечения

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

Понравилась статья? Поделить с друзьями:
  • Самокат kugoo m4 ошибка e 006
  • Самиздат цена ошибки
  • Самодиагностика ниссан х трейл т30 и коды ошибок
  • Самодиагностика эур калина коды ошибок
  • Самокат kugoo m4 pro ошибка е006