|
Правильный отбор данных для тестирования той или иной подпрограммы может значительно облегчить задачу обнаружения в ней ошибок. Прежде всего первый тест должен быть максимально прост, так как изначальная цель тестирования состоит в том, чтобы проверить, работает ли программа вообще. Такую проверку часто называют дымовым тестом, поскольку она напоминает вдувание дыма внутрь и наблюдение за тем, не выходит ли он где-нибудь наружу. Несколько первых тестов предназначено для проверки общей организации программы, поскольку, если она неудачна, затруднительно, да и нецелесообразно пробираться сквозь лабиринты программы.
Тест, который используется для проверки основной ветви программы, должен обнаруживать грубые ошибки. При наличии таких ошибок работа программы с огромной скоростью, равной нескольким миллионам операций в минуту, может пойти по любому направлению. Поэтому, если применить при первом испытании -сложный тест, а программа не будет нормально работать, мы не сможем определить, что тому виной: именно этот специальный пример или какая-то ошибка, проявляющаяся при любых других комбинациях данных. Сложный контрольный пример выявляет либо элементарные ошибки, которые могли бы быть обнаружены и более простыми средствами, либо неисправность оказывается такой же сложной, как и сам тест, выходные данные которого не позволяют установить причину отказа.
Упрощайте арифметические операции в тестах. Если, например, программа способна правильно складывать числа
11,11 + 22,22+33,33, она, несомненно, столь же успешно будет выполнять и сложение
12,56+45,92 + 34,79.
Простые тестовые данные облегчают ручной контроль результатов, а сами контрольные примеры предназначаются для проверки правильности работы программы, а совсем не для того, чтобы испытывать способность машины к выполнению арифметических действий.
Хотя для тестирования и рекомендуется использовать простые данные, но желательно также, чтобы каждый тестовый прогон давал максимально возможное количество информации, а общее число прогонов было незначительным. Однако при этом результаты испытаний отдельных элементов программы должны быть хорошо различимы, чтобы в случае ошибок не возникало путаницы. Хорошими следует считать такие тесты, которые помогают установить первопричину ошибок.
В некоторых случаях тестирования требуется производить "миниатюризацию" программы, т. е. разумно сокращать объем данных по сравнению с реальным. Можно, например, представить себе программу, которая работает обычно с матрицей размерностью 50X50, однако проверка соответствующих вычислений вручную при такой размерности неосуществима. Поэтому в качестве тестовых данных может быть использована матрица 5X5. Однако если такая миниатюризация программы требует внесения в нее каких-либо изменений, то могут возникнуть две нежелательные ситуации: либо существующие в программе ошибки в результате упрощающих изменений могут стать неявными или временно исчезнуть, либо могут появиться новые ошибки.
Точно так же, если некоторая подпрограмма работает в цикле 5 раз, она, безусловно, сможет работать и 105 раз (лишь бы хватало зарезервированного объема памяти). Часто бывает интересно посмотреть, какие результаты выдает программа при настройке ее на нуль, один или отрицательное число циклов; однако в последнем случае требуется особая осторожность. Обычно, если некоторый цикл предполагает выполнение N итераций, то наиболее распространенной ошибкой является организация N—1 или N+1 итераций.
Усложнение тестовых данных должно происходить постепенно. С каждым новым тестовым прогоном прибавляется еще один элемент программы, функционирующий правильно. Такая равномерность усложнений облегчает задачу распознавания ошибки при выдаче неверных результатов. Если же с помощью одного теста контролируются, сразу несколько непроверенных модулей программы, то в случае ошибки бывает трудно определить, какой из модулей привел к отказу.
Заключительные испытания должны обеспечить проверку всей программы в целом. Они предназначены для того, чтобы не пропустить подпрограмм с ошибками, которые при конкретных тестовых данных могли и не нарушить правильности результатов.
Цель тестирования любой программы состоит не в том, чтобы проверить, работает ли она при различных комбинациях тестовых данных, а в том, чтобы убедиться в ее правильной работе. Поэтому тестовые данные должны подбираться таким образом, чтобы программист был в состоянии вычислить правильный результат еще до начала тестового прогона. Если этого не сделать заблаговременно, то потом очень легко поддаться соблазну считать машинный результат достоверным.
5.10.1. ТИПЫ ТЕСТОВЫХ ДАННЫХ
Можно выделить три типа тестовых данных: создаваемые программистом, реальные модифицированные и реальные в полном объеме. Для тестирования обычно требуются данные всех трех типов.
В первую очередь используют данные, создаваемые программистом. Они подразделяются в свою очередь на контролируемые и случайные данные. Данные первого типа применяют для проверки общей работоспособности программы; они отличаются тем, что могут использоваться в редко встречающихся ситуациях. Благодаря такой возможности контролируемые данные позволяют осуществлять максимально эффективные проверки. Кроме того, правильный подбор тестовых данных должен способствовать уменьшению объема ручной работы по проверке машинных результатов. Недостатком контролируемых тестовых данных является то, что они предусматривают проверку лишь тех ситуаций, которые признаются существенными с точки зрения пользователя.
Поэтому не следует забывать об использовании данных другого типа (случайных тестовых данных), для формирования которых существуют специальные программы. Эти тестовые данные обладают тем преимуществом, что позволяют последовательно исключать ошибки из программы, а также обнаруживать ошибки, не выявляемые с помощью контролируемых тестовых данных. Отрицательной стороной использования случайных тестовых данных является трудность проверки правильности результатов. Хотя следует заметить, что если ошибки настолько серьезны, что приводят к авостам, то они, как правило, обнаруживаются.
Реальные модифицированные данные сочетают в себе до некоторой степени достоинства обоих отмеченных выше типов тестовых данных, создаваемых программистом. Путем аккуратного целенаправленного видоизменения реальных данных возможно осуществить целый ряд специальных режимов тестирования программы. Использование реальных данных вместо данных, создаваемых программистом, позволяет избежать специального формирования иногда весьма значительных по объему массивов информации.
Еще одно преимущество использования реальных модифицированных данных состоит в том, что в этом случае процесс тестирования приближается к практическим условиям работы. Это позволяет выявить множество проблем, которые не могут быть вскрыты с помощью данных, полученных искусственным путем. Одной из целей модификации реальных данных является проверка подпрограмм обработки ошибок. Умышленное введение ошибок в тестовые данные — это единственный способ проверить правильность функционирования таких подпрограмм.
Заключительные испытания проводятся на реальных данных в полном объеме. Если этот этап тестирования программного обеспечения может быть осуществлен параллельно с функционированием старой, не автоматизированной системы, то работа по ручной проверке машинных результатов существенно сокращается. Обычно при тестировании реальных данных программиста ожидает множество сюрпризов. Именно здесь выявляется недопонимание ("коммуникационный разрыв") между программистом и заказчиком и часто становятся явными их обоюдные просчеты.
Иногда параллельное функционирование новой и старой систем вскрывает те или иные дефекты старой системы. Но контроль результатов без такой параллельной работы становится чрезвычайно трудоемким, а то и вообще невозможным. Для того- чтобы испытания можно было бы считать завершенными, необходимо выполнить по крайней мере три параллельных тестовых прогона.
Параллельная работа старой и новой систем приводит к такому объему выходной информации, что визуальное сравнение результатов оказывается невозможным. В этих случаях пишут специальные программы, которые сравнивают записанные на ленту или диск результаты с требуемыми и устанавливают имеющиеся расхождения. Кроме того, эти программы позволяют сопоставлять старые и новые массивы данных результата в тех случаях, когда в программное обеспечение вносятся изменения.
Каждый из рассмотренных типов тестовых данных имеет свои достоинства и недостатки, но при совместном использовании они обеспечивают наиболее полное и качественное тестирование.
В ходе ручной проверки машинных результатов следует аккуратно проводить вычисления, чтобы в них не вкрались ошибки* В противном случае выходные данные, полученные с машины, будут сравниваться с неверными результатами ручного счета, что' может привести к большим затратам времени на поиск программной ошибки, в то время как корнем зла являются неверные вы* числения.
5.10.2. КЛАССЫ ТЕСТОВЫХ ДАННЫХ
В связи с тем что мы не располагаем средствами, Чтобы опробовать программу при всех возможных совокупностях данных,, желательно подобрать наиболее характерные данные. Для этого каждый новый тест должен содержать вполне определенный класс данных. Этот аспект тестирования часто оказывается вне сферы внимания разработчиков. Между тем правильная работа программы с данными конкретного класса позволяет надеяться на то, что она будет так же хорошо обрабатывать и любые другие данныр того, же класса.
Если, например, мы имеем дело с программой, которая должна считывать количество отработанных за неделю часов и начислять заработную плату, можно было бы установить несколько классов данных, характеризующих отработанное время: 0, 35, 40, 50, 100-Поскольку программа должна вычислять сверхурочную оплату, проверка ее работоспособности должна предусматривать задание времени, меньшего, большего и равного 40 ч. Кроме того, необходимо удостовериться, что программа правильно работает, когда время равно нулю или некоторому большому числу. Следует также посмотреть, как ведет себя программа при некоторых неправильных значениях отработанного времени, например при отрицательных величинах.
Для правильного определения необходимых классов тестовых данных обычно требуется некоторое знание программы. В предыдущем примере охвачены все возможные классы значений переменной "время". Введение каких-либо дополнительных значений, превышающих число 40, ничего не добавило бы к тому результату проверки, который получается при использовании числа 50, позволяющему судить о том, правильно ли работает программа при начислении сверхурочной оплаты. Однако, если бы какие-то из отмеченных классов были упущены, мы не могли бы иметь полной уверенности в работоспособности программы. Основная задача состоит в том, чтобы определить необходимые классы данных и сформировать 1—2 контрольных примера для каждого из них. Программисты-непрофессионалы склонны к избыточной проверке программ на одних классах данных и недостаточной — на других.
В каждом следующем тесте должен использоваться класс данных, отличный от предыдущего.
Использование тестовых данных определенных классов отвечает требованию экономичности тестирования.
5.10.3. АНАЛИЗ РЕЗУЛЬТАТОВ ТЕСТИРОВАНИЯ
Каждый раз, когда используются тестовые данные, необходимо иметь какой-то способ проверки правильности машинных результатов. Существует несколько таких способов:
1) вычисление результатов вручную;
2) получение результатов из регистрационной книги, документации или совокупности таблиц и
3) получение результата с помощью некоторой другой машинной программы.
Важно, чтобы проверяемые машинные результаты сопоставлялись с соответствующими данными, взятыми из другого, независимого источника. Очевидно, что наименее желательно применение первого метода вследствие большого объема ручной работы.
Все три отмеченных метода проверки основываются на знании конкретных значений результатов, которые должны быть получены. Однако это не всегда возможно или целесообразно. Для таких случаев можно прибегнуть к двум дополнительным методам, применение которых, однако, требует от испытателя знания алгоритма, реализованного в программе.
Для использования первого из этих дополнительных методов необходимо знание пределов изменения выходных переменных. Обычно такие пределы можно довольно легко определить, если проанализировать программу, зная ее алгоритм. Любые значения, выходящие за ожидаемые пределы, должны подвергаться более тщательной проверке.
Второй метод состоит в целенаправленном манипулировании тестовыми данными и предсказании на основе продуманного и осторожного их изменения направления и пределов изменения выходных переменных. Совпадение фактических и прогнозируемых изменений результатов может в какой-то мере говорить о правильности работы программы.
5.10Д. ФОРМИРОВАНИЕ ТЕСТОВЫХ ДАННЫХ
Усовершенствование процесса тестирования связано с применением способов генерирования тестовых данных. В случае использования перфокарт такие методы очень просты и основываются на введении специальных стандартных тестовых перфокарт:
1. Перфокарт, заполненных нулями в позициях 1-9, единицами в позициях 10-19, двойками в позициях 20-29, и т. д.
2. Перфокарт, содержащих 1 в позиции 1,
2 в позиции 2,
9 в позиции 9,
0 в позиции 10,
1 в позиции 11,
9 в позиции 19,
0 в позиции 20,
1 в позиции 21, и т. д.
3. Перфокарт, содержащих во всех 80 позициях нули, Перфокарт, содержащих во всех 80 позициях единицы,
Перфокарт, содержащих во всех 80 позициях девятки. 4.,Перфокарт, в которых единицы чередуются с нулями. 5. Перфокарт, содержащих во всех 80 колонках буквенные символы:
А в позиции 1, Б в позиции 2, В в позиции 3, и т. д.
Тестовые данные такого вида могут применяться для контроля правильности идентификации полей данных. Например, если для ввода информации используются колонки 27-32, то в качестве теста целесообразно применять карты видов 1 и 2. В этом случае результат тестирования должен при правильной работе программы выглядеть следующим образом:
222333 789012
где каждый столбец читается как двузначный номер, идентифицирующий позиции входных данных (т. е. 27, 28,...,32). Этот тест обеспечивает проверку одного из самых важных условий — правильности считывания программой нужных позиций перфокарт. После этого могут быть использованы тесты видов 3-5 для проверки правильности обработки нулевых и буквенных данных. Еще один метод проверки правильности идентификации полей данных заключается в использовании определенной буквы или цифры для каждого поля. При таком способе легче различать границы полей данных.
Конечно, наивно полагать, что применение тестовых перфокарт разрешает все проблемы тестирования. Однако с их помощью можно выполнить много разнообразных простых проверок. Такие карты должны находиться в ящике-стенде для хранения перфокарт и быть легкодоступными для программистов.
Для языков КОБОЛ и ПЛ/1 характерна интенсивная работа с файлами данных, поэтому необходимо иметь тесты для проверки файлов. Для формирования таких тестовых файлов обычно можно применять программы-утилиты. Например, фирма IBM предлагает программу-утилит IEBDG (генератор данных), которая обеспечивает получение контрольных примеров, могущих использоваться для тестирования рабочих программ. В ряде вычислительных комплексов имеются специальные генераторы тестовых данных, которые формируют испытательные файлы. Основной принцип работы таких генераторов заключается в использовании описаний файлов, содержащихся в испытываемых программах для построения тестовых файлов. Тестовые генераторы могут как создаваться собственными силами пользователей, так и поставляться фирмами, разрабатывающими программное обеспечение ЭВМ.
5.10.5. ЭТАПЫ ТЕСТИРОВАНИЯ
Процесс тестирования программы можно разделить на три этапа:
1. Проверка в нормальных условиях.
2. Проверка в экстремальных условиях.
3. Проверка в исключительных ситуациях.
Эти этапы должны быть обеспечены всеми необходимыми средствами тестирования.
Каждый из трех этапов проверки должен гарантировать получение верных результатов при правильных входных данных и выдачу сообщений об ошибках при неправильных данных.
Проверка в нормальных условиях
Проверка в нормальных условиях предполагает тестирование на -основе данных, которые характерны для реальных условий функционирования программы. Случаи, когда программа должна работать со всеми возможными данными, чрезвычайно редки. Обычно имеют место конкретные ограничения на область изменения данных, в которой программа должна сохранять свою работоспособность. Поэтому проверка в нормальных условиях должна показать, что программа выдает правильные результаты для характерных совокупностей данных.
Проверка в экстремальных условиях
Этот этап тестирования должен идти сразу за проверкой программы в нормальных условиях. Тестовые данные, этого этапа включают граничные значения области изменения входных переменных, которые должны восприниматься программой как правильные данные. Для нецифровых данных необходимо использовать подобные типичные символы, охватывающие все возможные ситуации. Для цифровых данных в качестве экстремальных условий следует брать начальное и конечное значения допустимой области изменения переменной при одновременном изменении длины соответствующего поля от минимальной до максимальной. Типичными примерами таких экстремальных значений являются очень большие числа, очень малые числа и отсутствие информации. Каждая программа характеризуется своими собственными экстремальными данными, которые должны подбираться программистом.
Процесс использования экстремальных значений переменных в качестве тестовых данных носит название граничных испытаний. Граничные испытания зачастую предоставляют наилучшие возможности для выявления ошибок. Если некоторая программа работает правильно в граничных условиях, обычно это означает, что она будет нормально работать и в любой другой области значений переменных.
Еще один тип экстремальных условий — это граничные объемы данных, когда они состоят из слишком малого или, наоборот, слишком большого числа записей. Необходимо установить, что происходит с программой, если ей на обработку не поступает ни одного элемента данных или только один, и сохранит ли она в этих условиях свою работоспособность.
Особый интерес представляют так называемые нулевые примеры. Для цифрового ввода — это обычно нулевые значения вводимых данных; для последовательностей символов — это цепочка пробелов или нулей; для указателей — нулевое значение указателя. Нулевые примеры представляют собой один из лучших тестов, поскольку они имитируют состояние данных, которое время от времени имеет место в реальных условиях эксплуатации программы. Если подобное тестирование не выполняется, то впоследствии часто приходится сталкиваться с непонятным поведением программы.
Не всегда, однако, легко определить, какие значения данных являются экстремальными. Рассмотрим, например, программу, которая должна считывать четыре одноразрядных положительных целых числа. Поверхностный подход к построению тестов приводит в данном случае к формированию следующих экстремальных условий:
 На первый взгляд эти условия действительно представляются экстремальными, хотя на самом деле таковыми не являются. Это хороший тест, но он является экстремальным только в том случае, когда выполняется следующее действие:
^ ANSWER=A+B + C+D Однако если предстоит вычислить
ANSWER = (A+±y*D то наиболее вероятное значение ответа получится при С=1: ' ANSWER = [-Ц^- У * 9 = 18*
в то время как при С=9
ANSWER = (-^-^ *9=2*
Следовательно, правильный подбор численных значений переменных для создания экстремальных условий возможен только в случае знания алгоритма решения задачи. Приведенный пример иллюстрирует часто допускаемую ошибку тестирования, когда забывают, что существуют экстремальные значения как входных, так и выходных переменных. Главная цель использования экстремальных тестов состоит в установлении того факта, что поля данных промежуточных результатов имеют размеры, достаточные для проведения требуемых вычислений.
Условия, необходимые для тестирования программы, можно создавать также путем введения в нее определенных констант. Этот способ хорош тогда, когда возникает необходимость тестирования экстремальных условий, являющихся результатом длительных вычислений, но трудно подобрать контрольные данные для проверки тех ситуаций, которые представляют интерес. Например, если вы желаете установить момент, когда результаты вычислений становятся отрицательными, а эта ситуация возникает относительно редко, то можно добавить в программу оператор, который в конце проверки присваивает интересующей вас переменной отрицательный знак. Однако впоследствии надо не забывать об исключении этой искусственной модификации из программы. Один простой способ запоминания этого требования заключается в том, чтобы иметь в программе комментарий, который указывал бы, что данный оператор является тестовым. Можно также отперфорировать в позициях 73—80 тестовой строки исходной программы слово ТЕСТ как напоминание о необходимости исключения этой строки ш окончании тестирования.
Проверка в исключительных ситуациях.
Последний этап тестирования программы проводится с использованием данных, значения которых лежат за пределами допустимой области изменения. Известно, что все программы разрабатываются в расчете на обработку какого-то ограниченного набора данных. Поэтому важно получить ответ на вопросы: что произойдет, если программе, не рассчитанной на обработку отрицательных или нулевых значений переменных, в результате какой-либо ошибки придется иметь дело как раз с такими данными? Как будет вести себя программа, работающая с массивами, если количество их элементов превысит величину, указанную в описании? Что случится, если цепочки символов окажутся длиннее или-короче, чем это предусмотрено? Что произойдет, если числа будут слишком большими или, наоборот, слишком малыми?
Наихудшая ситуация складывается тогда, когда программа воспринимает неверные данные как правильные и выдает неверный, но правдоподобный результат. В этом случае представляются неубедительными отговорки, что программа не была предназначена для обработки данных, приведенных к такому результату, или что неверные данные не должны вводиться в машину. Такие ограничения не могут решить проблему, поскольку неверные данные могут образоваться в результате опечаток или неправильного понимания оператором инструкций по вводу данных. Поэтому программа должна сама отвергать любые данные, которые она не в состоянии обрабатывать правильно.
Если вы являетесь единственным пользователем программы, то можете не вводить в нее операторов, отвергающих недопустимые данные. Однако, если та же программа предназначена еще и для других пользователей, такие операторы крайне необходимы. Они должны проходить обязательную проверку в процессе тестирования, чтобы программист убедился в наличии всех необходимых операторов редактирования данных.
В отношении проверки программы в исключительных и экстремальных условиях можно дать ряд рекомендаций, которые могут сэкономить время программиста. Прежде всего, если предусматриваются проверки правильности редактирования данных, берите значения, расположенные на обеих границах допустимой области; именно эти значения являются наиболее вероятными источниками затруднений при работе программы.
Данные, содержащие пробелы, цифры и буквы, должны опробоваться в самых разнообразных сочетаниях. Пробуйте заполнять пробелами и буквами те поля, которые требуют цифровых данных. При проверке взаимных связей между различными полями данных попытайтесь перемежать верные и неверные данные.
Иногда большие затруднения вызывают данные, содержащие сразу несколько ошибок. Еще один вид ошибок, представляющих особый'интерес, — это ошибка в первом или последнем элементе обрабатываемой информации. Важно, чтобы при наличии такой ошибки программа не заканчивала работу, как в нормальной ситуации. Для этого достаточно при тестировании сформировать файл, состоящий из единственного элемента, который неверен. Если такая работа не проделана на стадии тестирования программы, трудности неизбежно возникнут при ее эксплуатации. По окончании отмеченных выше проверок наступает время удовлетворения любознательности и свободного полета фантазии программиста. Попробуйте использовать в качестве входных данных программы необычную информацию: небольшую пачку карт из приемного кармана, неупорядоченную колоду перфокарт, ленту с данными, не относящимися к вашей программе. Помните, что первое правило в системах обработки данных гласит: при использовании неверных входных данных результаты будут неправильными. Если ваша программа не защищена от использования неверной информации, вы можете потратить массу времени на поиски программных ошибок, в то время как истинной причиной отказа являются сами обрабатываемые данные. Хотя оператор, допускающий из-за небрежности ошибки при подготовке данных, достоин порицания, но и программисту непростительно создавать программу, которая воспринимает неверные данные как правильные.
Испытывайте программу в нормальных, экстремальных и исключительных условиях.
5.10.6. тестирование ветвей
Большую пользу в процессе тестирования приносят блок-схемы программ, показывающие возможные пути работы алгоритма. Блок-схемы можно использовать для установления критических ветвей программы и определения необходимых классов данных для проверки каждой ветви. Применение методов структурного программирования позволяет упростить тестирование ветвей, поскольку последних при этом становится меньше.
Подготавливайте тестовые данные для проверки каждой ветви алгоритма.
Проверку ветвей легче всего осуществлять при тестировании отдельных модулей. Если же ее отложить до этапа испытаний связанных модулей, тестирование ветвей становится более трудным делом. Рассмотрим модуль, в котором проверке подлежат п ветвей. Предположим далее, что существует второй модуль, который обращается к первому в т точках. Если каждый из этих модулей допускает независимую проверку, то необходимо опробовать всего т+п путей. Если же эти два модуля испытываются совместно, то число подлежащих проверке путей работы программы становится равным ту^п. Кроме того, чем больше размер испытываемой части программы, тем труднее определить местонахождение обнаруженной ошибки.
Следует, однако, помнить, что простое прослеживание всех возможных путей работы программы не обеспечивает необходимой полноты ее тестирования. Предположим, например, что программа должна определить, равны ли между собой три числа, и в ней использован следующий алгоритм:
IF ((X+Y+Z)/3=Y)
THEN PRINT 'X, Y, Z, EQUAL' ELSE PRINT 'X, Y, Z, NOT EQUAL'
Ясно, что тестовые данные, которые лишь проверяют факт работоспособности каждой из двух ветвей этой программы, приведут к тому, что она ошибочно будет считаться правильной.
⇐5.9. Методы тестирования || Оглавление || 5.11. Примеры тестов⇒
|