Реферат на тему:

Тестування та налагодження програм

Тестування та налагодження програм

Тестування виявляє лише наявність, але ніяк не відсутність помилок.

Е. Дейкстра

Програма, вільна від помилок — це абстрактне теоретичне поняття.

Мало в якому виді діяльності існує стільки можливостей для помилок, як у
програмуванні. Одним з критеріїв професійної майстерності програмістів є
їх спроможність виявляти та виправляти власні помилки. Програмування –
це досить складна задача. Ми намагались описати різні технології
програмування, мета яких, в першу чергу, зробити програми структурними і
зрозумілими. Але жодна з цих технологій не здатна в корені змінити
сумного факту – помилки в програмі зустрічаються завжди. Ми знаходимо
їх з допомогою тестування, а усуваємо з допомогою налагодження.
Починаючі програмісти не вміють цього робити, досвідчені – вміють, але
помилки роблять усі без виключень. Як ні важко з цим змиритись, але
хороші програмісти знають, що основний час при програмуванні буде
витрачений на тестування та налагодження. Нижче ми обговоримо, як можна
скоротити час на налагодження програми і як зробити цей процес більш
технологічним. Визначимо зміст ключових слів даного розділу.

Налагодження (рос. отладка) – це процес, який починається з моменту
встановлення існування помилки і закінчується локалізацією цієї помилки
в програмі, тобто визначенням її характеру та місцезнаходження. Таким
чином, налагодження програми передбачає обов’язкову наявність помилки.

Тестування – це виконання комплексу вправ (завдань) для перевірки
працездатності програми за будь-яких умов. Тестування може виявити факт
наявності помилки, а налагодження виявляє причину помилки, так що ці два
етапи розробки “перекриваються”.

Налагодження програм.

Налагодження програм – досить складний процес. По-перше, для виправлення
помилки необхідно повністю виявити її причини, які часто далеко
неочевидні. По-друге, ця діяльність психологічно носить негативний
характер, в тому розумінні, що програміст повинен визнати, що саме його
помилка є причиною програмного збою. Крім того, налагодження – це
процес, який призупиняється лише тимчасово, поки тестування не виявить
наявність чергової помилки.

Отже, що робити, коли програма не працює, або працює, але видає
неправильні результати? Зі стратегічної точки зору треба мати на увазі,
що існують три основні методи налагодження програми, кожен з яких має
свої особливості.

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

Переважне використання програмних засобів комп’ютера – так званих
налагоджувачів (англ. debuggers) для пошуку помилок. Як варіант цього
метода можна розглядати трасування, або можливість покрокового виконання
програми і слідкування за значеннями змінних в процесі виконання
програми, яке є доступним у деяких програмних середовищах. Цей метод не
гарантує знаходження всіх помилок, тому що програмний налагоджувач може
працювати більш коректно, ніж компілятор – наприклад, обнуляти
невизначені змінні або інакше розподіляти пам’ять.

Поєднує створення програми з одночасним налагодженням та тестуванням її
частин. Цей метод вимагає високої самодисципліни програміста і є більш
ефективним у випадку аналітичного програмування (програмування згори
донизу).

Важко сказати, якому підходу слід надавати перевагу. Скоріше за все це
визначається характером програми та особистими прихильностями її автора.

Захисне програмування

Отже, помилка – це реальне і неминуче явище в будь-якій програмі. Тому
цілком природною виглядає принцип захисного програмування [9]. Захисне
програмування – це методологія створення програм, яка зменшує
ймовірність помилок в програмі. Основна ідея полягає в тому, щоб
передбачити в програмі якомога більше засобів контролю помилок.

Лозунгом захисного програмування може вважатися відомий закон Мерфі:

Все, що може зіпсуватися, псується обов’язково. Все, що не може
зіпсуватися, псується також.

Принцип захисного програмування спирається на два основних поняття:
захист і стійкість до помилок (в першу чергу невірних даних).

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

Найпростіший метод – це тотальний контроль. При цьому програма
збільшується в об’ємі порівняно з технічним завданням за рахунок
введення додаткових модулів і операторів для перевірки. Контролю в першу
чергу підлягають:

вхідні дані (дуже багато програмних помилок пов’язано з невірним
зчитуванням вхідних даних за рахунок помилок набору, невірних форматів і
т.д. Тому введені дані дуже бажано роздруковувати одразу після їх вводу.
Цей процес називається перевіркою відлунням (рос. эхо-проверкой));

всі звертання з інших програм, включаючи “рідні” підпрограми;

проміжні дані, які передаються з інших програм;

записи бази даних;

результати обчислень (своєї ж програми). Тут можливі помилки за рахунок
переповнення або втрати точності.

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

Нехай треба обчислити значення змінної b = (1.0/3)*3. З урахуванням
обмеженості розрядів це дасть b = 0.3333*3 = 0.9999. Зрозуміло, що у
такому випадку перевірка (b = 1) завжди даватиме результат false.

Нехай x = 999.0, y = -1000, z = 0.0001. З урахуванням обмеженості
розрядів маємо парадоксальний результат:

((x + y) + z) + 1 = ((-1) + 0.0001) + 1 = 0.0001, але

(x + (y + z)) + 1 = 999 + (-1000) + 1 = 0.

Подібні проблеми виникають тоді, коли додаються числа, які близькі до
мінімальних і максимальних для розрядної сітки.

Деякі типові помилки в програмах.

Що стосується деяких видів типових помилок, яких може припустись
розробник програми, то вони можуть бути такими:

вибраний невірний алгоритм (наприклад, для чисельного розв’язання систем
лінійних алгебраїчних рівнянь існують різні методи, зокрема, метод
Гаусса, метод Зейделя, метод прогонки і т.д. Ці методи є збіжними при
виконанні певних (своїх для кожного метода) умов, які накладаються на
систему рівнянь, і цей факт обов’язково необхідно враховувати при виборі
метода в кожному конкретному випадку);

помилки аналізу (невірне програмування правильного алгоритму);

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

помилки при виконанні операцій (наприклад, ділення на нуль, втрата
точності, вихід за межі типу даних);

помилки даних (наприклад, символьні замість числових);

неініціалізовані змінні; змінні без початкових значень – часта помилка в
програмах, яку важко знайти debugger’ом, оскільки останній якраз може
проініціалізувати змінну;

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

індексація з виходом за межі масиву, тобто використання як елементів
масиву змінних, що знаходяться поза його межами;

непередбачені особливі випадки вводу-виводу – наприклад, коли не
обробляється сигнал кінця файлу.

І якщо про синтаксичні помилки піклується компілятор, то помилки,
перелічені вище, можуть бути виявлені лише на етапі тестування.

Методологічні принципи налагодження програм.

Забудьте про свою самовпевненість (див. закон Мерфі).

Використовуйте невеликі модулі (оскільки кількість і складність помилок
зростає експоненційно в залежності від довжини програми).

Слід зауважити, що згідно з цим принципом виникає проблема: із
зростанням кількості модулів зростає кількість зв’язків між ними, а
отже, збільшується і кількість можливих помилок. Єдиний вихід з цієї
проблеми – структурне програмування.

Дублюйте тести. Дуже добре, коли при повторі тесту ви отримуєте ту саму
помилку. Найгірші помилки – ті, які то з’являються, то зникають.

“Зрозумій себе”. Якщо ви знаєте, що досить часто припускаєтесь однієї й
тієї самої помилки, можливо й на цей раз ви зробили те саме.

Підхід до відшукання помилок повинен бути систематичним. Не обмежуйтесь
випадковим зондуванням своєї програми – це може тільки зробити ситуацію
ще більш незрозумілою. Головне, щоб кожний наступний тест, спрямований
на локалізацію помилки, давав би чергову інформацію про неї. Одним із
прикладів систематичного підходу до пошуку помилок є так званий
дихотомічний пошук (відомий також під героїчною назвою “ловля лева в
пустелі”). Він базується на тому, що при кожному черговому тесті з
програми виключається та половина програмного коду, де помилка напевне
не проявляється. Проаналізувавши результати тестового прогону, знову
виключається частина програми, і так до тих пір, поки помилка не буде
повністю локалізована.

Користуйтеся принципом розумної недовіри. Якщо програма працює давно і
успішно, це ще зовсім не означає, що в ній немає помилок! Можливо,
просто програма ще не була поставлена перед тестом, який виявить
приховану помилку.

Контролюйте правдоподібність даних. Деякі типові приклади помилок:

змішуються символьні і числові дані;

дані виходять за межі можливої області значень змінних;

елементи інформації, які повинні мати фіксовану довжину (індекс, номер
телефона), не відповідають цій умові;

податок перевищує суму, на яку він нараховується…

8. Спілкуйтеся з колегами. Вже не кажучи про те, що інші можуть
побачити те, чого не бачите ви, є ще один важливий момент: дуже часто
людина сама знаходить свою помилку, коли намагається комусь пояснити,
які саме дії вона хоче виконати.

Тестування програм.

Найголовніший принцип в тестуванні програм полягає в тому, що про цю
стадію потрібно думати під час всього періода розробки програми.
Створюючи черговий фрагмент програми, обов’язково треба мати на увазі
тест, який міг би перевіряти коректність його роботи. Якщо немає
відповіді на питання про спосіб тестування даного фрагменту, можливо
потрібно розбити його на підпрограми, які тестувати легше, або просто
переписати.

Проблемним є також і питання про те, коли можна призупинити тестування,
вважаючи програму правильною. Один із корисних принципів полягає в тому,
щоб кожний оператор програми був би випробуваний принаймні раз, тобто
тестові дані повинні забезпечувати перевірку всіх можливих умов
виникнення помилок. Потрібно перевірити кожну гілку алгоритму. Тестова
інформація повинна включати в себе всі типи даних. Такі перевірки (які є
необхідними, але недостатніми), при яких тести проходять по всіх гілках
логічної схеми програми, називаються тестуванням гілок. Тим не менш,
слід мати на увазі, що абсолютно повного обсягу тестування добитись
надто важко. Детальніші пояснення з цього приводу можна знайти в [5].

Якість тестування визначається значним чином не кількістю тестових
прогонів. Головне, щоб кожний черговий тестовий прогін контролював би
щось таке, що не було перевірено в попередніх прогонах. Задача
тестування полягає в тому, щоб створити для програми максимально
напружений режим роботи.

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

Перший тест може бути зовсім простим. Його головна мета – перевірити, чи
спрацює програма взагалі. Тому його називають ще “димовим” тестом.
Подальше ускладнення тестів має відбуватись поступово, додаючи до
перевірених елементів програми при кожному тесті по одному. Якщо з
допомогою одного тесту ви намагаєтесь перевірити відразу декілька
підпрограм або вузлів програми, то при виникненні помилки її важко буде
локалізувати.

Деякі принципи тестування програм.

Використовуйте підхід захисного програмування. Тобто передбачайте ще під
час написання програми обробку ситуацій, які “не можуть статись ні за
яких обставин”. І під час тестування обов’язково переконайтесь, що
програма, захищена від некоректних даних. Хорошим прикладом тут може
служити перевірка на нульовий або навіть від’ємний розмір масиву в
програмі обчислення середнього значення елементів цього масиву.

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

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

обчисліть для порівняння результат іншим способом, наприклад, на
калькуляторі;

використайте табличні дані;

порівняйте з результатом роботи іншої програми, яка обчислює той самий
результат іншим способом;

в деяких випадках можна спробувати обернути результат тесту – чи
одержите ви вхідні дані (така ситуація можлива, наприклад для програм
шифровки або стискання інформації) ?

Оцінюйте обсяг тестів. Тестування не можна вважати завершеним, поки
кожний оператор програми не буде перевірено.

Деякі прийоми тестування.

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

Інший ефективний прийом тестування називають стресовим тестуванням –
програма перевіряється дуже великими об’ємами вхідних даних,
сгенерованих комп’ютером, наприклад, датчиком випадкових чисел. Такі
випадкові дані можуть бути дуже ефективними для перевірки можливості
переповнень масивів, файлів, змінних-лічильників.

Інші види тестів базуються на зумисному використанні некоректних даних.
Будь-який блок програми, який одержує вхідні дані ззовні, повинен
перевіряти їх, перш ніж використовувати. Історія програмних помилок
включає дуже багато не тільки курйозних, але і сумних епізодів. Про один
з них можна прочитати в [10]: “В листопаді 1998 року в журналі
Scientific American був описаний інцидент, що стався на борту
американського ракетного крейсера Yorktown. Член команди помилково
замість значущого числа ввів 0, що призвело до помилкового ділення на
нуль. Помилка розрослася і врешті решт силова установка корабля
виявилася виведеною з ладу. Декілька годин Yorktown дрейфував волею
хвиль – а все через те, що в програмі не було здійснено перевірку
діапазону вхідних даних.”

Види тестових даних.

Таким чином, ретельний підхід до тестування вимагає ретельного ж
готування тестових даних. Можна виділити три основних види тестових
даних.

Тестові дані готуються програмістом. До них відносяться спеціально
підготовані контрольовані дані та випадкові дані, сгенеровані деякою
програмою.

Реальні дані, модифіковані спеціальним чином, щоб перевірити програми
контролю вхідних даних (наприклад, в реальні дані вноситься помилкова
інформація).

Реальні дані в повному об’ємі можна використовувати для заключних
тестових випробувань.

Кожний з описаних видів тестових даних має свої переваги та недоліки,
але їх спільне використання здатне забезпечити необхідний мінімум
тестування вашої програми. При цьому процес тестування програми може
бути розподілений на три етапи:

перевірка в нормальних умовах (тестові дані знаходяться всередині
допустимого діапазона значень);

перевірка в критичних умовах (тестові дані наближаються до меж
допустимого діапазона значень);

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

Кожний з трьох етапів повинен гарантувати отримання вірних результатів
при вірних вхідних даних і повідомлення про помилку (причому з
деталюванням виду помилки) при невірних даних.

Нижче в таблиці наведено приклади тестування програми, яка повинна
обчислювати довжину головної діагоналі прямокутного паралелепіпеда із
сторонами A, B, C.

Номер тесту Тестові дані

(A, B, C) Коментар

1 1, 1, 1 Димовий тест

2 1, 2, 3 Нормальні умови

3 0, 0, 0 Нульовий тест (результат повинен дорівнювати нулю!)

4 0, 1, 2 Критичні умови

Це не паралелепіпед! Що видасть програма?

5 1, 2, 0

6 1, 0, 2

7 1, -6, 3 Екстремальні умови (невірні дані). Що видасть програма?

Нагадаємо головне правило тестування – програми необхідно тестувати!

І на завершення, оптимістичний прогноз: кожна остання помилка, знайдена
в програмі, є насправді передостанньою.

Бажаємо успіхів в створенні та тестуванні ваших програм!

ЛІТЕРАТУРА

Н. Вирт. Систематическое программирование. – М.: Мир, 1977. – 183 с.

Ален И. Голуб. С и С++. Правила программирования. – М.: БИНОМ, 1996. –
272 с.

У. Дал, Э. Дейкстра, К. Хоор. Структурное программирование. – М.: Мир,
1973. – 247 с.

Э. Дейкстра. Дисциплина программирования. – М.: Мир, 1978. – 275 с.

Д. Ван Тассел. Стиль, разработка, эффективность, отладка и испытание
программ. – М.: Мир, 1985. – 332 с.

М. Зелковиц, А. Шоу, Дж. Гэннон. Принципы разработки программного
обеспечения. – М.: Мир, 1982. – 368 с.

Г. С. Иванова. Основы программирования: Учебник для вузов. – М.: Изд-во
МГТУ им. Н.Э. Баумана, 2002. – 416 с.

Г. С. Иванова. Технология программирования: Учебник для вузов. – М.:
Изд-во МГТУ им. Н.Э. Баумана, 2002. – 320 с.

Э. Йодан. Структурное проектирование и конструирование програм. – М.:
Мир, 1979. – 415 с.

Б. Керниган, Р. Пайк. Практика программирования. – СПб.: Невский
диалект, 2001. – 381 с.

Э.Б. Коффман. Turbo Pascal. – М.: Издательский дом «Вильямс», 2002. –
896 с.

Похожие записи