Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Программа дает правдоподобные результатыСодержание книги
Похожие статьи вашей тематики
Поиск на нашем сайте
Это самая коварная ситуация. Получив результаты, близкие к предполагаемым, вы можете подумать, что программа работает правильно, и передать ее в эксплуатацию. Как убедиться в том, что результаты правильны, а не правдоподобны? Здесь помогает тестирование программы. Тщательно подобранные тесты позволят отличить правдоподобные значения от правильных результатов. Как правило, одного набора тестов недостаточно для убедительного доказательства неверности работы программы. Специалисты-тестировщики всегда подбирают несколько наборов тестов так, чтобы точнее выявить дефекты программы. Выявить правдоподобные результаты часто помогает прием, заимствованный из математики. Обычно мы проверяем правильность вычисления корня уравнения, подставляя его в уравнение и убеждаясь в том, что левая часть уравнения равна правой его части. Тем не менее, математикам давно известно, что из того, что левая часть уравнения, при подстановке в него приближенно вычисленного корня, почти совпадает с правой частью, вовсе не вытекает, что этот корень близок к настоящему корню уравнения. Особенность таких некорректных задач заключается в том, что небольшое изменение исходных данных приводит к сильному изменению результата, хотя такой сильный скачок не вытекает из условий задачи. Это дает способ проверки правильности правдоподобных результатов. При тестировании программы надо провести серию испытаний с близкими значениями исходных данных и следить за тем, как меняются результаты ее работы. Неоправданно сильное изменение результатов при незначительном изменении данных должно вызвать сомнение в правильности вычислений, если только это не вытекает из условий задачи. Локализация ошибки После обнаружения ошибки надо найти место ее возникновения, как говорят, локализовать ошибку. Это не такая простая задача, как кажется поначалу. Ошибка может быть сделана где-то в начале исходного текста программы, а обнаружена позже, спустя несколько десятков строк исходного текста или даже в другой функции. Для локализации приходится делать обратный просмотр исходного текста программы, начиная от места обнаружения ошибки. В этом помогает трассировка программы, расстановка контрольных точек и пошаговое выполнение. Контрольная точка устанавливается в некотором отдалении от места обнаружения ошибки, в том месте, где по вашим соображениям может начаться неправильное выполнение программы. После этого тщательно проверяется участок программы от контрольной точки до места обнаружения ошибки. Иногда такое обратное отслеживание хода выполнения программы не помогает. Тогда приходится прослеживать ее работу по исходному тексту, просматривая выполнение операторов со значениями, полученными из контрольной печати, и доходя до места обнаружения ошибки. Такое прослеживание часто называют прокруткой программы. Прокрутка программы — трудоемкое занятие, ее лучше проводить на небольшом участке программы, сузив предварительно участок прокрутки с помощью отладочной печати. Когда и прокрутка не помогает, приходится проверять логику выполнения программы. Проверка начинается с места обнаружения ошибки. Просматривается полученная в этой точке отладочная печать, и проводятся рассуждения вроде следующих: "Такие результаты мог дать оператор А или оператор В. Но оператор В в этом случае не может выполняться. Значит, посмотрим на оператор А. Оператор А дал значение С, а оно могло получиться только в ситуации D. Чтобы возникла ситуация D, надо, чтобы...". На каждом логическом шаге приходится делать отладочную печать и небольшую прокрутку. Привлечение логических рассуждений помогает быстрее проделать эту трудную работу, поскольку концентрирует внимание только на тех переменных, которые могут получить ошибочные значения, а таких переменных немного. Наконец, каждый программист знает, что при попытке объяснить ошибку, которая никак не поддается локализации, своему коллеге по работе или постороннему консультанту, неожиданно, в процессе рассказа, самому автору программного кода вдруг становится ясно, в чем заключается ошибка. Иногда даже письменное изложение проблемы помогает найти ее решение. Это объясняется тем, что при последовательном изложении проблемы мысли приходят в порядок, выстраивается логическая цепочка, приводящая к решению задачи. Поэтому очень важно периодически обсуждать возникающие проблемы всей командой разработчиков. Такие обсуждения не только позволяют устранять трудности, возникающие в процессе разработки, но и держат программистов в курсе всех задач, решаемых на каждом этапе реализации проекта. Устранение ошибки После того как ошибка обнаружена и найдено место ее возникновения, наступает время исправления программы. Некоторые ошибки, такие как неправильно записанные выражения, неверно определенные начальные значения переменных, неправильные входы и выходы из циклов, ошибки в логических условиях, исправить легко. Для этого достаточно изменить один или несколько операторов в исходном коде программы. После этого надо снова запустить те же наборы тестов, чтобы убедиться в том, что ошибки исчезли. Другие ошибки, например, те, которые возникают из-за неправильно запрограммированного алгоритма, исправить сложнее. Их устранение требует изменения больших кусков программного кода. Такие изменения зачастую вносят в программу новые ошибки. Эти ошибки снова надо обнаруживать, локализовать и устранять. Процесс отладки становится циклическим. Его приходится повторять несколько раз. Как гласит шутливая аксиома отладки: "Каждая последняя ошибка является предпоследней". Чтобы не попасть в неприятный цикл отладки или быстрее выйти из него, следуйте простому правилу: "Исправлять за один раз только одну ошибку". Тогда вы будете знать, из-за чего возникла новая ошибка, и сможете быстро устранить ее. Труднее всего исправить ошибки алгоритма, заложенного в программу. Изменение алгоритма часто влечет переработку структуры программы, введение новых классов или значительное изменение существующих классов. Происходит возврат к этапу проектирования. Наконец, не так уж редко встречается ошибка в определении требований к программе. Программа выдает не те результаты, которые ожидает заказчик программного продукта, да и вообще не то, что он хотел. Можно считать это не ошибкой, а недоговоренностью или недоразумением, но такое положение надо исправлять. Для исправления приходится возвращаться к самому первому этапу разработки и начинать все сначала, уточняя требования к программе. Средства отладки Трудоемкость процесса отладки всегда вызывала стремление автоматизировать его. С появлением первых компиляторов стали появляться и программы- отладчики (debuggers), называемые на жаргоне программистов "дебаггерами". Они предоставляют программные средства для выполнения основных работ по отладке. С их помощью легко установить контрольные точки, сделать трассировку и пошаговое выполнение программы, просмотреть текущие значения всех или выбранных переменных. Отладчики тесно связаны с компиляторами. Для улучшения отладки компилятор может вставлять в машинный код дополнительную, отладочную, информацию, которую отладчик использует при прогоне программы. Поэтому отладчики чаще всего поставляются вместе с компиляторами, в одной интегрированной среде разработки IDE (Integrated Development Environment). В меню Options, или каком-нибудь другом меню, предназначенном для настройки параметров компилятора в такой интегрированной среде, можно выбрать один из нескольких режимов работы компилятора, в том числе отладочный режим, Debug, или окончательный режим, Release. Во время разработки программы надо выбрать отладочный режим компилятора. Тогда можно будет использовать все средства отладчика, которые обычно перечислены в меню Debug интегрированной среды. После окончания отладки, когда уже решено передавать программу в эксплуатацию, компилятор надо перестроить на создание окончательной версии (release, build). В режиме Release компилятор удаляет из машинного кода отладочную информацию и генерирует оптимизированный рабочий код. Упражнения 1. Приведите примеры ошибок, которые выявляются не сразу, а после выполнения нескольких следующих операторов. 2. Приведите примеры ошибок, которые не может обнаружить компилятор. 3. Вам нужно найти ошибку в программе, вычисляющей корни квадратного уравнения по обычной школьной формуле. Расставьте в исходном коде отладочную печать. 4. Выясните, каково наибольшее и наименьшее целое число на вашем компьютере. Что происходит при выходе за эти значения? 5. Найдите наибольшее по абсолютной величине действительное число, которое ваш компьютер воспринимает как ноль ("машинный ноль") и ответьте на вопрос, почему сравнение с нулем действительных чисел, вроде if (х == 0.0), часто приводит к ошибке. 6. Проверьте, при всех ли значениях х выполняется тождество cos2(jc) + sin2 (jc) = 1 на вашем компьютере. 7. Найдите с максимальной возможной на вашем компьютере точностью две суммы, отличающиеся порядком следования слагаемых: 5[ = 1 + —+ — +...+—-—н 2 3 999999 1000000 и 1 1 1, S2 =- +-------- +... + - + 1. 1000000 999999 2 Посмотрите, совпадут ли значения ij и 02- Тестирование Мы уже не раз говорили, что тестирование заключается в проверке работы программы на ряде задач, решение которых известно заранее. Результаты работы программы сравниваются с эталонными результатами, и на основании их совпадения или несовпадения делается вывод о применимости программы к этому классу задач. Классическое определение тестирования, данное Гленфордом Дж. Майер- сом (Glenford J. Myers), звучит так: "Тестирование — процесс выполнения программы с намерением найти ошибки". Новейшие методы тестирования не только запускают программу с различными исходными и промежуточными данными, но и проверяют ее исходный текст. Поэтому определение Майерса уже устарело и под тестированием сейчас понимаются любые действия, направленные на поиск ошибок в программе. Разумеется, тестирование не может доказать отсутствие ошибок, оно только выявляет их наличие. Нельзя сказать, что программа, успешно прошедшая тестирование, свободна от ошибок. Тем не менее, после серьезного тестирования можно запускать программу в промышленную эксплуатацию с большой долей уверенности. Практика показывает, что полностью протестированный программный продукт успешно справляется со своей задачей. Тестирование не следует отождествлять с отладкой, хотя они тесно связаны. Задача тестирования — выявить наличие дефектов в программе, а задача отладки — отыскать их местоположение и устранить ошибки. Сначала с помощью тестирования надо найти ошибки, а потом, во время отладки, устранить их. Иногда тестирование и отладка выполняются одним специалистом в одном процессе, чаще их осуществляют разные люди. Unit-тестирование По сути дела, тестированием своего участка программы занимается каждый разработчик программного обеспечения. Написав какой-либо класс, процедуру или несколько процедур, программист обязательно проверяет их работу на характерных для этого кода наборах данных. Появившаяся недавно методика unit-тестирования возводит эту привычку в абсолютное правило. Эта методика предписывает проводить тестирование после написания каждой процедуры или класса. Более того, unit-тестирование обязывает писать тесты еще до создания исходного кода программы! Допустим, мы решили написать класс комплексных чисел complex. По методике unit-тестирования мы начинаем с того, что пишем пустой класс complex и вместе с ним сразу же пишем класс Testcomplex, который будет впоследствии содержать тесты. class Complex{}; class TestComplex{ Complex z; public: TestComplex(Complex z){ this.z = z;
} void runTest(){ } }; Весь этот код сразу компилируется, чтобы убедиться в правильности написанной конструкции. Затем начинаем разработку класса complex. В процессе разработки каждый создаваемый метод класса complex записывается еще и в тестовый класс Testcomplex, например, следующим образом: #include <iostream> class Complex{ double re, im; public: Complex(double a = 0.0, double b = 0.0){ re = a; im = b; } double mod(){ return sqrt(re * re + im * im); } }; class TestComplex{ Complex z; public: TestComplex(Complex z) { this.z = z; } void runTest(double result){ cout «testMod(result) «endl; } bool testMod(double result){ return z.modO == result; } >; void main(){ Complex zl(), z2(0.0, 1.0), z3(3.0, -4.0); TestComplex tl(zl), t2(z2), t3(z3); tl.runTest(0.0); t2.runTest(1.0); t2.runTest(5.0); } После компиляции и отладки этого кода сразу же начинается тестирование. Только после того, как все тесты выполнены успешно, продолжается создание класса. Такая методика программирования получила название программирования, управляемого тестами (TDP, test-driven programming). Она вошла обязательной составной частью в быстро получившую популярность среди разработчиков свободного программного обеспечения методику экстремального программирования ХР (extreme programming). Unit-тестирование первоначально возникло в технологии Java. Не удивительно, что и наибольшее развитие получил свободно распространяемый программный продукт JUnit, автоматизирующий unit-тестирование Java-ioiaccoB. Для разработчиков, пишущих программы на языке С++, по аналогии с этим программным продуктом, создана библиотека классов, названная CppUnit. Сейчас в распоряжении программистов есть множество программных продуктов, облегчающих unit-тестирование. Более того, есть продукты, генерирующие наборы тестов. Например, фирмой Parasoft Corporation создан и распространяется программный продукт JTest, подготавливающий тесты для JUnit. Методики тестирования За полувековую историю развития компьютеров предложено множество методик тестирования. Самая простая рассматривает программу как "черный ящик", внутренняя структура которого неизвестна. Испытатель проверяет работу "черного ящика", подавая на его вход определенные сигналы и наблюдая, как программа реагирует на них. Первым эту методику предложил, пожалуй, Козьма Прутков, сказавший: "Щелкни кобылу в нос, она махнет хвостом". По методике "черного ящика" каждый тест задает исходные данные программе. Программа выполняется, после чего полученные результаты сравниваются с заранее известными тестовыми значениями. Такая методика покажет отсутствие ошибок только в том случае, когда набор тестов охватит все возможные исходные данные. Поскольку их необъятно много, всеобъемлющий охват исходных данных невозможен. Следовательно, теоретически нет уверенности, что такая методика выявит все ошибки. Кроме того, две ошибки могут взаимно уничтожаться на тестах и не будут выявлены по этой методике. Другая методика обращается с программой как с "белым ящиком" или, другими словами, "прозрачным ящиком". Она учитывает исходный текст программы. По этой методике набор тестов должен привести к выполнению каждого оператора программы и прохождению каждого из возможных путей выполнения программы хотя бы по одному разу. Эта методика хорошо проверяет логику выполнения программы, но тоже не может выявить все ошибки. Например, некоторые ветви программы могут быть просто упущены в процессе разработки, а такое тестирование не выявит их отсутствие. Реальные методики тестирования используют оба подхода. Одни методики приближаются к "черному ящику", другие — к "прозрачному ящику". Так или иначе, в настоящее время практически во все наборы тестов включаются тесты, учитывающие исходный текст программы. Это значительно облегчает тестирование. Пусть, например, надо протестировать программу, находящую площадь треугольника по длинам его сторон а, b, с. Если мы знаем, что эта программа вычисляет отдельно площадь прямоугольного треугольника как полупроизведение его катетов, и отдельно площадь остальных треугольников по формуле Герона, то нам не надо создавать огромное количество тестов. Мы можем разбить все множество исходных данных а, b, с на два класса — образующих прямоугольный треугольник и не образующих его — и прогнать всего по одному тесту для каждого класса. По своему назначению тесты делятся на несколько групп: □ функциональное тестирование; □ тестирование обращений к базам данных; □ тестирование логики программы; □ нагрузочное тестирование; □ стрессовое тестирование; □ тестирование интерфейса пользователя; □ тестирование безопасности и прав доступа; □ тестирование инсталляции программного продукта. Рассмотрим подробнее каждую из этих групп тестов. Функциональное тестирование Это основной вид тестирования, проверяющий соответствие программы функциональным требованиям, предъявленным к ней на самом первом этапе проектирования программного продукта. Функциональное тестирование призвано убедиться в правильности ввода исходных данных, обработки и вывода результатов, а также в правильности работы всех элементов управления программой. Для этого проигрываются все сценарии использования программы (use case). При этом в тестах задаются и правильные и неправильные входные данные. Это позволяет проверить реакцию программы на ошибки ввода. В большинстве функциональных тестов программа рассматривается как "черный ящик", поскольку проверяется работа программы, а не ее реализация. Тем не менее, как уже говорилось выше, знание алгоритма и исходного текста программы может помочь в проверке правильного ее функционирования. Пусть, например, надо протестировать процедуру, решающую квадратное уравнение ax +Ьх + с = 0 с действительными коэффициентами. Для этого надо подготовить тестовый набор коэффициентов а, b, с, учитывающих все возможные ситуации. Не зная о том, что решение уравнения зависит от знака дискриминанта, мы можем упустить один из тестов, приводящих к положительному или отрицательному дискриминанту. Зато, зная алгоритм нахождения корней, мы можем разбить все тестовые значения коэффициентов на те, которые дают неотрицательный дискриминант, и те, для которых дискриминант отрицателен. В каждой группе можно взять только один набор значений коэффициентов а, b, с. Дополнив этот набор вырожденными и граничными значениями, мы получим полный набор тестов. Он показан в табл. 20.1.
Успешно выполнив этот набор, мы можем быть уверены, что обычный школьный алгоритм решения квадратного уравнения работает правильно. Конечно, такую уверенность дает только простота задачи, которую решает тестируемая программа. В реальных условиях убедиться в полноте набора тестов гораздо труднее.
|
|||||
Последнее изменение этой страницы: 2016-12-11; просмотров: 417; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.220.134.161 (0.013 с.) |