2.1. Введення.
Поняття процесу програмування якісно змінилися.
Виробництво програм набуло масового характеру, що істотно
збільшилися їх обсяг і складність. Розробка програмних комп -
комплексом потребувала значних зусиль великих колективів
фахівців. Програми перестали бути тільки обчислювальними
і почали виконувати найважливіші функції з управління та обробці
інформації в різних галузях.
Розвиток і застосування технологій проектування комплексів
програм призводить до необхідності вимірювання та порівняння їх ефектив -
бництва перш за все за ступенем впливу на якість прог -
раммного продукту.
Забезпечення високої якості складних комплексів програм
пов'язане зі значними витратами праці розробників. Затру -
ти на створення програм швидко збільшуються при зростанні
вимог, причому для складних комплексів вельми складно дос -
тічь високої якості функціонування, і після забезпечення
загальної працездатності можуть знадобиться роки праці для напів -
чення необхідних показників якості. Тому вже сьогодні
потрібні методи і засоби, які дозволили б помітно по -
висіть якість програм програм при відносно невисоких
затратах праці.
2.2. Обгрунтування вибору технології тестування.
Як відомо, під час створення типового програмного проекту
близько 50% загального часу і більше 50% загальної вартості витраті -
ється на перевірку (тестування) розробляється програми або
системи. Крім того, частка вартості тестування в загальній стои -
мости програм має тенденцію зростати при збільшенні складність
ності комплексів програм і підвищення вимог до їх качест -
ву.
З огляду на це, при відпрацюванні технології тестування прог -
Рамм слід чітко виділяти певний (по можливості не
дуже велика) число правил налагодження, що забезпечують високу
якість програмного продукту і знижують витрати на його ство -
даніе.
Тестування - це процес виконання програми з метою
виявлення помилок. Одним із способів вивчення поставленого
питання є дослідження стратегії тестування, називає -
мій стратегією чорного ящика, тестуванням з управлінням у
даними, або тестуванням з управлінням по входу-виходу. При
використанні цієї стратегії програма розглядається як
чорний ящик. Тестові дані використовуються тільки у відповідних
вії зі специфікацією програми (тобто без урахування знань про її
внутрішньої структури).
При такому підході виявлення всіх помилок у програмі є при -
ляется критерієм вичерпного вхідного тестування. Послід -
неї може бути досягнуто, якщо в якості тестових наборів
використовувати всі можливі набори вхідних даних. Слідчий -
але, ми приходимо до висновку, що для вичерпного тестування
програми потрібно нескінченне число тестів, а значить побудувати -
ение вичерпного вхідного тесту неможливо. Це підтверджують
ється двома аргументами: по-перше, не можна створити тест, гаран -
тірующій відсутність помилок, по-друге, розробка таких тес -
тов суперечить економічним вимогам. Оскільки ісчерпи -
БЕЗПЕЧУЮТЬ тестування виключається, нашою метою має стати мак -
сімізація результативності вкладення капіталовкладень в тести -
вання (максимізація числа помилок, що виявляються одним тес -
том). Для цього необхідно розглядати внутрішню структуру
програми і робити деякі розумні, але, звичайно, не облад -
ющіе повною гарантією вірогідності припущення.
Стратегія білого ящика, чи стратегія тестування, управ -
ляемого логікою програми, дозволяє досліджувати внутрішню
структуру програми. У цьому випадку тестує отримує тісто -
ві дані шляхом аналізу логіки програми.
Порівняємо спосіб побудови тестів при даній стратегії з
вичерпним вхідним тестуванням стратегії чорного ящика.
Невірно припущення, що достатньо побудувати такий набір
тестів, у якому кожен оператор виконується хоча б одна
разів. Вичерпного вхідного тестування може бути поставлене -
але у відповідність вичерпне тестування маршрутів. Підрив -
зумевается, що програма повністю перевірена, якщо з допомогою
тестів вдається здійснити виконання цієї програми з усіх
можливим маршрутами її потоку (графа) передач управління.
Останнє твердження має два слабкі пункти: по-перше,
число не повторюють один одного маршрутів - астрономічне;
по-друге, навіть якщо кожен маршрут може бути перевірений, сама
програма може містити помилки (наприклад, деякі маршрути
пропущені).
У результаті всіх викладених вище зауважень можна отме -
тить, що ні вичерпне вхідне тестування ні вичерпують -
загальне тестування маршрутів не можуть стати корисними стратегія -
ми, тому що обидва вони не реалізовуються. Тому реальним шляхом,
який дозволить створити хорошу, але, звичайно не абсолютну
стратегію, є поєднання тестування програми несколь -
кими методами.
2.3. Розробка технологічного процесу тестування.
Якщо відмовитися від тестування всіх шляхів, то можна поки -
мовити, що критерієм покриття є виконання кожного опе -
ратора програми принаймні один раз.
Як приклад візьмемо тестування модуль Param.
Призначення модуля - розбирати командний рядок з параметрами -
рами на окремі параметри.
Об'єктом тестування оберемо правило ParamStr об'єкта
Parameters.
function Parameters.ParamStr (ParamNum: byte): string;
begin
if ParamNum = 0 then
if Delux then
ParamStr: =''
else
if Lo (DosVersion)> = 3 then
ParamStr: = system.ParamStr (0)
else
ParamStr: =''
else
ParamStr: = OptionStr (ParamNum);
end;
Схема алгоритму цієї функції:
--------------------¬
| Початок |
L --------- T ----------
|
/
/ Ні
/ ParamNum ----------------¬
= 0/|
/ ----------+---------¬
/ да | ParamStr = |
| | OptionStr (ParamNum) |
/ L --------- T ----------
так/|
-<-----------/ Delux |
| = True/|
----------+-------¬/|
| ParamStr =''|/ні |
L --------- T -------- | |
|/|
|/Lo (немає |
|/DosVersion --------------¬ L -------- ¬
|)> = 3/| |
|/| |
|/Да | |
| -----------+---------¬ ---------+--------¬ |
| | ParamStr = System. | | ParamStr =''| |
| | ParamStr (0) | L -------- T --------- |
| L ---------- T ---------- | |
L ---------------- >+<-------------------+<------- ---
----------+---------¬
| Кінець |
L --------------------
Рис 2.1.
Табл. 2.1.
г === T ================== T =================== T ====== ==============¬
| N | Вхідні дані | Очікуваний результат | Отриманий результат |
|---+------------------+-------------------+------ --------------|
| 1 | ParamNum = 1 | ParamStr = | ParamStr = |
| | | OptionStr (ParamNum) | OptionStr (ParamNum) |
|---+------------------+-------------------+------ --------------|
| 2 | ParamNum = 0 | ParamStr =''| ParamStr =''|
| | Delux = true | | |
|---+------------------+-------------------+------ --------------|
| 3 | ParamNum = 0 | ParamStr = | ParamStr = |
| | Delux = false | System.ParamStr (0) | System.ParamStr (0) |
| | Lo (DosVersion) = 3 | | |
|---+------------------+-------------------+------ --------------|
| 4 | ParamNum = 0 | ParamStr =''| ParamStr =''|
| | Delux = false | | |
| | Lo (DosVersion) = 2 | | |
L ===|==================|===================|====== ==============-
Даний критерій тестування гірше, ніж здається на перший
погляд. Наприклад, якщо умова Lo (DosVersion)> = 3 буде оші -
Бочна записано Lo (DosVersion)> 3. Під час тестування по данно -
му критерію ця помилка не буде виявлена.
Більш сильний критерій покриття логіки програми відомий
як покриття рішень, або покриття переходів. Згідно данно -
му критерієм має бути записано достатню кількість тестів, та -
таке, що кожне рішення на цих тестах набуде значення істина
і брехня принаймні один раз.
Можна показати, що покриття рішень зазвичай задовольняє
критерієм покриття операторів. Оскільки кожен оператор лежить
на певному шляху, що виходить з оператора переходу, або з
точки входу програми, при виконанні кожного напряму пе -
рехода кожен оператор повинен бути виконаний. Отже,
тести наведені вище підходять і для цього критерію.
Однак існують винятки, наприклад, оператор case. У
цьому операторі можливі не двозначні рішення.
CASE умова OF
m1: оператор1;
m2: оператор2;
m3: оператор3
ELSE
m4: оператор4
END
Критерієм для таких випадків є виконання кожного
можливого результату всіх рішень по крайней мере один раз.
Кращим критерієм в порівнянні з попереднім є покриття -
нення умов. У цьому випадку записують кількість тестів, достатній -
ное для того, щоб всі можливі результати кожної умови
у вирішенні виконувалися принаймні один раз.
Розглянемо приклад на функції OptionStr.
function Parameters.OptionStr (ParamNum: byte): string;
var
I, Len: Byte;
begin
Len: = 0;
I: = OptPosition (ParamNum);
if I <> 0 then
while (I <= SLen) and not (ParStr [I] in OptDelim) do
begin
Inc (Len);
OptionStr [Len]: = ParStr [I];
Inc (I);
end;
OptionStr [0]: = Char (Len);
end;
Алгоритм цієї функції:
--------------------¬
| Початок |
L --------- T ----------
----------+---------¬
| Len = 0; |
| I = OptPosition (|
| ParamNum) |
L --------- T ----------
/
/ Да
/ I = 0 ----------------¬
/ |
/ |
/ ні |
----------------+ |
|/|
|/|
|/I <= SLen да |
|/І не ------------->+
| ParStr (I) в/|
| OptDelim/|
|/|
|/Ні |
| ----------+---------¬ |
| | Len = Len + 1; | |
| | OptionStr (Len) = | |
| | ParStr (I) | |
| L --------- T ---------- |
L ---------------- |
------------------------
----------+---------¬
| Кінець |
L --------------------
Рис 2.2.
Функція містить три умови:
I = 0, I <= SLen, not (ParStr [i] in OptDelim).
Отже, потрібна достатня кількість тестів, таке,
щоб реалізувати ситуації, де I = 0, I <> 0 в першу умови і
I <= SLen, I> SLen, (ParStr [i] in OptDelim) = true, (ParStr [i] in
OptDelim) = false в другому умови.
Тести, що задовольняють критерію покриття умов піведени
у таблиці 2.2. (нехай стоку параметрів має вигляд: MAIN.GRM
/ Q/P, SLen = 13, ParamNum = 1):
Табл. 2.2.
г === T ================== T =================== T ====== ==============¬
| N | Вхідні дані | Очікуваний результат | Отриманий результат |
|---+------------------+-------------------+------ --------------|
| 1 | I = 0 | OptionStr (0) = 0 | OptionStr (0) = 0 |
| | | | |
|---+------------------+-------------------+------ --------------|
| 2 | I = 1 | OptionStr (0) = 0 | OptionStr (0) = 0 |
| | (ParStr [i] in | | |
| | OptDelim) = true | | |
|---+------------------+-------------------+------ --------------|
| 3 | I = 1 | OptionStr (0) = 8 | OptionStr (0) = 8 |
| | (ParStr [i] in | | |
| | OptDelim) = false | | |
|---+------------------+-------------------+------ --------------|
| 4 | I = 11 | OptionStr (0) = 0 | OptionStr (0) = 0 |
| | (ParStr [i] in | | |
| | OptDelim) = true | | |
|---+------------------+-------------------+------ --------------|
| 5 | I = 11 | OptionStr (0) = 0 | OptionStr (0) = 0 |
| | (ParStr [i] in | | |
| | OptDelim) = false | | |
L ===|==================|===================|====== ==============-
Хоча застосування критерію покриття умов на першому
погляд задовольняє критерію покриття рішень, це не завжди
так. Якщо тестується рішення
if A and B then ...
то при критерії покриття умов потрібні були б два тести:
A = true, B = false і A = false, B = true. Але в цьому випадку
не виконувалося б then-пропозиція оператора if.
Існує ще один критерій, названий покриттям рі -
ний/умов. Він вимагає такого достатнього набору тестів,
щоб всі можливі результати кожного умови у вирішенні ви -
виконується принаймні один раз, всі результати кожного ре -
ності виконувалися принаймні один раз і кожній точці входити -
да передавалося управління принаймні один раз.
Недоліком критерію покриття рішень/умов є не -
можливість його застосування для виконання всіх результатів
всіх умов; часто таке виконання має місце в следст -
умові того, що певні умови приховані іншими умовами.
Наприклад, якщо умова AND це неправда, то ніяке з подальші -
щих умов у виразі не буде виконано. Аналогічно, якщо
умова OR є істина, то ніяке з наступних умов не
буде виконано. Отже, критерії покриття умов і
покриття рішень/умов недостатньо чутливі до помилок
в логічних виразах.
Критерієм, який вирішує ці та деякі інші пробле -
ми, є комбінаторне покриття умов. Він вимагає ство -
ня такого числа тестів, щоб всі можливі комбінації різу -
льтатов умови в кожному рішенні і всі крапки входу виконуємо -
лись принаймні один раз.
Розглянемо правило CheckTreeNil в модулі TmObejct об'єкта
Main.
procedure Main.CheckTreeNil;
var
tn: boolean;
begin
tn: = (GetPtrOfClass (SCl) = nil) and
(GetPtrOfClass (UCl) = nil) and
(GetPtrOfClass (ACl) = nil);
if tn then Error ( 'не знайдено жодного нетермінал');
end;
Алгоритм процедури:
--------------------¬
| Початок |
L --------- T ----------
|
/
/
/
/ G (SCl) = nil
/ І немає
/ G (UCl) = nil -----------¬
і/|
G (ACl) = nil/|
/ |
/ |
/ Да |
| |
--------------------+------------------¬ |
| Error ( 'не знайдено жодного нетермінал') | |
L ------------------- T ------------------- |
+<----------------------
----------+---------¬
| Кінець |
L --------------------
Рис 2.3.
Для того, щоб протестувати цю процедуру необхідно
вісім тестів, хоча вона покривається всього двома шляхами.
Табл. 2.3.
г === T =========================== T ============ T ==== ========¬
| N | Вхідні дані | Очікуваний | Отриманий |
| | | Результат | результат |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) = nil | | |
| 1 | GetPtrOfClass (UCl) = nil | tn = true | tn = true |
| | GetPtrOfClass (ACl) = nil | | |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) <> nil | | |
| 2 | GetPtrOfClass (UCl) = nil | tn = false | tn = false |
| | GetPtrOfClass (ACl) = nil | | |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) = nil | | |
| 3 | GetPtrOfClass (UCl) <> nil | tn = false | tn = false |
| | GetPtrOfClass (ACl) = nil | | |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) <> nil | | |
| 4 | GetPtrOfClass (UCl) <> nil | tn = false | tn = false |
| | GetPtrOfClass (ACl) = nil | | |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) = nil | | |
| 5 | GetPtrOfClass (UCl) = nil | tn = false | tn = false |
| | GetPtrOfClass (ACl) <> nil | | |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) <> nil | | |
| 6 | GetPtrOfClass (UCl) = nil | tn = false | tn = false |
| | GetPtrOfClass (ACl) <> nil | | |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) = nil | | |
| 7 | GetPtrOfClass (UCl) <> nil | tn = false | tn = false |
| | GetPtrOfClass (ACl) <> nil | | |
|---+---------------------------+------------+---- --------|
| | GetPtrOfClass (SCl) <> nil | | |
| 8 | GetPtrOfClass (UCl) <> nil | tn = false | tn = false |
| | GetPtrOfClass (ACl) <> nil | | |
L ===|===========================|============|==== ========-
У разі циклів кількість тестів для задоволення критерію
комбінаторного покриття умов зазвичай більше, ніж число пу -
тей.
Легко бачити, що набір тестів, що задовольняє критерію
комбінаторного покриття умов, задовольняє також і крите -
ріям покриття рішень, покриття умов і покриття решеній/ус-
ловій.
Таким чином, для програм, що містять тільки одне усло -
віє на кожне рішення, мінімальним є критерій, набір
тестів якого:
- Викликає виконання всіх результатів кожного рішення з
принаймні один раз;
- Передає управління кожній точці входу (наприклад, опера -
тор CASE).
Для програм, що містять рішення, кожне з яких має
більше одного умови, мінімальний критерій складається з набору
тестів, що викликають всіх можливих комбінацій результатів усло -
вий у кожному рішенні і передають управління кожній точці входити -
та програми принаймні один раз.
У світлі всього вищевикладеного, можна зобразити алгоритм
вибору мінімального критерію, за яким необхідно тестую -
вати програму (див. рис. 2.4.).
--------------------¬
| Початок |
L --------- T ----------
--------------------------------->+| ----------+---------¬
| | Вибір оператор |
| | Умовного переходу |
| L --------- T ----------
| /
| /
|/Немає
|/Це оператор ---------¬
| IF/|
|/|
|/|
|/Да |
| | |
|/|
|/|
| Так/Умова немає |
| ----------/ Містить --------->+
| | Більше одного/|
| | Комп-та/|
| |/|
| |/|
| ----------------+---------------¬ ---------------- +-------------¬
| | Набір тестів, що викликає все | | Набір тестів, що викликає |
| | Можливі комбінації резуль-в | | виконання всіх результатів |
| | Умов у кожному рішенні не | | кожного рішення не менш |
| | Менше одного разу. | | Одного разу. |
| L --------------- T ---------------- L --------------- T --------------
| L ---------------> T <----------------
| /
|/Це
| Немає/останній.
L ---------------------------/оператор
умовного /
переходу /
/
/ да
----------+---------¬
| Кінець |
L --------------------
Рис 2.4.