Тема. Модульне тестування
1. Модульне тестування
Модульне тестування — це тестування програми на
рівні окремо узятих модулів, функцій або класів.
Модульне тестування проводиться за принципом
"білого ящика", тобто грунтується на знанні внутрішньої структури
програми, і часто включає ті або інші методи аналізу покриття коду.
Модульне тестування
зазвичай має на увазі створення
кожного модуля певного середовища, що включає заглушки для всіх інтерфейсів
тестованого модуля. Деякі з них можуть використовуватися для подачі вхідних
значень, інші для аналізу результатів, присутність третіх може бути
продиктована вимогами, що накладаються компілятором і компоновщиком.
На рівні модульного тестування найпростіше виявити дефекти, пов’язані з
алгоритмічними помилками і помилками кодування алгоритмів, наприклад, роботи з
умовами і лічильниками циклів, а також з використанням локальних змінних і
ресурсів. Помилки, пов’язані з невірним трактуванням даних, некоректною
реалізацією інтерфейсів, сумісністю, продуктивністю і т.д. зазвичай пропускаються на рівні модульного тестування
і виявляються на пізніших стадіях тестування.
У даному випадку аналізується
етап модульного тестування. Якщо аналіз не дав потрібної інформації, наприклад,
За способом виконання структурним тестуванням або тестуванням "білого
ящика", модульне тестування характеризується тим, що тести виконують або
покривають логіку програми (вихідний текст). Тести, пов’язані із структурним
тестуванням, будуються за наступними принципами:
ü На основі аналізу потоку управління. У цьому випадку
елементи, які мають бути покриті під час проходження тестів, визначаються на
основі структурних критеріїв тестування С0, С1, С2. До них відносяться вершини,
дуги, шляхи керуючого графа програми (КГП), що управляє умовами, комбінацією умов
і т.д.
ü На основі аналізу потоку даних, коли елементи, які
мають бути покриті, визначаються за допомогою потоку даних, тобто
інформаційного графу програми.
Тестування на основі потоку даних. Цей вигляд тестування направлений на виявлення аномалій потоку даних. Запропонована там стратегія вимагала
тестування всіх взаємозв’язків, тобто потрібне покриття дуг інформаційного графа програми. Недолік стратегії в тому, що вона не
включає критерій С1 і не гарантує покриття рішень.
Методи проектування тестових шляхів для досягнення заданої міри
тестованості в структурному тестуванні.
Процес побудови набору тестів при
структурному тестуванні прийнято ділити на три фази:
ü Конструювання КГП.
ü Вибір тестових шляхів.
ü Генерація тестів,
відповідних тестовим шляхам.
Перша фаза відповідає статичному аналізу програми, завдання якого полягає в
програми і залежного від нього і від критерію
тестування безлічі елементів, які необхідно покрити тестами.
На третій фазі за відомими шляхами тестування здійснюється пошук
відповідних тестів, що реалізовують проходження цих шляхів.
Друга фаза забезпечує вибір тестових шляхів. Виділяють три підходи до
побудови тестових шляхів:
ü Методи шляхів, що
реалізовуються.
Статичні методи. Найпростіший метод — побудова кожного шляху за
допомогою поступового його подовження за рахунок додавання дуг, поки не буде
досягнута вихідна вершина графа програми. Ця ідея може бути посилена в так званих
адаптивних методах, які кожного разу додають лише один тестовий шлях (вхідний тест),
використовуючи попередні шляхи (тести) як керівництво для вибору подальших
доріг відповідно до деякої стратегії. Найчастіше адаптивні стратегії
застосовуються по відношенню до критерію С1. Основний недолік статичних методів полягає в тому, що не враховується
можливість або не можливість реалізації побудованих тестових
шляхів.
Динамічні методи. Такі методи
передбачають побудову повної системи тестів, що задовольняють заданому
критерію, шляхом одночасного рішення задачі побудови покриваючої
безлічі шляхів і формування тестових даних. При цьому можна автоматично
враховувати можливість реалізації або не реалізації раніше розглянутих шляхів
або їх частин. Основною ідеєю динамічних методів є
під’єднування до початкових реалізованих відрізків шляхів їх подальших частин
так, щоб:
Ø не втрачати при цьому знов отриманих доріг;
Ø покрити необхідні елементи структури програми.
Методи шляхів, що реалізовуються. Дана методика полягає у виділенні з безлічі шляхів підмножини
всіх шляхів, що реалізовуються. Після чого покриваюча безліч шляхів будується з
отриманої підмножини шляхів, що реалізовуються.
Перевага статичних методів полягає в порівняно невеликій кількості
необхідних ресурсів, як під час використання, так і під час розробки. Проте їх
реалізація може містити непередбачуваний відсоток браку (шляхів, що не
реалізовуються). Крім того, в цих системах перехід від покриваючої великої
кількісті шляхів до повної системи тестів користувач повинен здійснити вручну,
а ця робота досить трудомістка.
Динамічні методи вимагають значно більших ресурсів як під час розробки, так
і під час експлуатації,
Перевага цих методів полягає в тому, що їх продукція має деякий якісний
рівень, який реалізовується за допомогою шляхів. Методи шляхів, що
реалізовуються, дають найкращий результат.
Інтеграційне тестування — це тестування частини системи, що складається з двох і більш за модулів.
Основне завдання інтеграційного тестування — пошук
дефектів, пов’язаних з помилками в реалізації і інтерпретації інтерфейсної
взаємодії між модулями.
З технологічної точки зору інтеграційне
тестування є кількісним розвитком модульного,
оскільки так само, як і модульне тестування, оперує
інтерфейсами модулів і підсистем і вимагає створення тестового оточення, включаючи заглушки (Stub) на місці відсутніх
модулів. Основна різниця між модульним і інтеграційним тестуванням полягає в цілях, тобто в типах
дефектів, що виявляються, які, у свою чергу, визначають стратегію вибору
вхідних даних і методів аналізу. Зокрема, на рівні інтеграційного
тестування часто застосовуються методи, пов’язані з покриттям
інтерфейсів, наприклад, викликів функцій або методів, або аналіз
використання інтерфейсних об’єктів, таких як глобальні ресурси, засоби
комунікацій, що надаються операційною системою.
Мал. 8. Приклад структури комплексу програм
На Мал.
8 приведена структура комплексу програм K,
що складається з модулів M1, M2,
M11, M12, M21, M22, які були відтестовані на етапі модульного тестування. Завдання, що вирішується методом інтеграційного тестування, — тестування міжмодульних
зв’язків, що реалізовуються під час виконання програмного забезпечення
комплексу K. Інтеграційне тестування використовує модель "білого
ящика" на модульному рівні. Оскільки тестувальникові текст програми
відомий з детальністю до виклику всіх модулів, що входять в тестований
комплекс, вживання структурних критеріїв на даному етапі можливо і виправдано.
Інтеграційне тестування застосовується на етапі
складання модулів, що модульно протестовані в єдиний комплекс.
Два методи складання модулів:
Ø Монолітний, такий, що характеризується
одночасним об’єднанням всіх модулів в тестований комплекс.
Ø Інкрементальний, що характеризується кроковим
(помодульним) нарощуванням комплексу програм з кроковим тестуванням складеного комплексу. В
інкрементальном методі виділяють дві стадії додавання модулів:
ü "Зверху вниз"
і відповідне йому висхідне тестування.
ü "Від низу до
верху" і відповідно низхідне тестування.
Особливості монолітного тестування полягають в наступному: для заміни неопрацьованих до моменту тестування модулів,
окрім самого верхнього (мал.
8), необхідно додатково розробляти драйвери
( test driver ) і заглушки ( stub ), які заміняють відсутні протестовані модулі нижніх рівнів.
Порівняння монолітного
та інкрементального підходу дає наступне:
v Монолітне тестування вимагає великих трудовитрат, пов’язаних з додатковою
розробкою драйверів і заглушок і зі складністю ідентифікації помилок, що
виявляються в просторі зібраного коду;
v Покрокове тестування
пов’язане з меншою трудомісткістю ідентифікації помилок за рахунок поступового
нарощування об’єму тестованого коду і відповідно локалізації доданої області
тестованого коду;
v Монолітне тестування надає великі можливості розпаралелювання робіт
особливо на початковій фазі тестування.
Особливості низхідного
тестування полягають в наступному:
ü організація середовища
для виконуваної черговості викликів модулями тестованих модулів, що вже
протестували,
ü постійна розробка і
використання заглушок,
ü організація
пріоритетного тестування модулів, що містять операції обміну з оточенням, або
модулів, критичних для тестованого алгоритму.
Наприклад, порядок тестування комплексу K (мал.
8) при низхідному тестуванні може бути таким, як
показано в прикладі 12,
де тестовий набір, розроблений для модуля Mi,
позначений як XYi = (X, Y)i
1) K—>XYK
2) M1—>XY1
3) M11—>XY11
4) M2—>XY2
5) M22—>XY22
6) M21—>XY21
7) M12—>XY12
Приклад 12. Можливий порядок тестів при низхідному
тестуванні
Недоліки низхідного тестування:
v Проблема розробки
достатньо "інтелектуальних" заглушок, тобто заглушок, придатних до
використання під час моделювання різних режимів роботи комплексу, необхідних
для тестування.
v Складність організації і
розробки середовища для реалізації виконання модулів в потрібній послідовності.
v Паралельна розробка
модулів верхніх і нижніх рівнів призводиться до не завжди ефективної реалізації
модулів із-за спеціалізації ще не
тестованих модулів нижніх рівнів до модулів верхніх рівнів, що вже
відтестували.
Особливості висхідного тестування в організації порядку збірки і переходу
до тестування модулів, відповідного порядку їх реалізації.
Наприклад, порядок тестування комплексу K (мал.
8) при висхідному тестуванні може бути наступним (приклад 13).
1) M11—>XY11
2) M12—>XY12
3) M1—>XY1
4) M21—>XY21
5) M2(M21, Stub(M22))—>XY2
6) K(M1, M2(M21, Stub(M22))—>XYK
7) M22—>XY22
8) M2—>XY2
9) K—>XYK
Приклад 13. Можливий порядок тестів при висхідному тестуванні
Недоліки висхідного тестування:
ü Запізнювання перевірки
концептуальних особливостей тестованого комплексу.
ü Необхідність в розробці
і використанні драйверів.
Особливості інтеграційного тестування для процедурного програмування
Процес побудови набору тестів під час структурного тестування визначається
принципом, на якому грунтується конструювання Графа Моделі Програми (ГМП). Від
цього залежить безліч тестових шляхів
Першим підходом до розробки програмного забезпечення є процедурне (модульне)
програмування. Процедурне програмування передбачає написання вихідного
коду в імперативному (наказовому) стилі, приписуючому певну послідовність
виконання команд, а також опис програмного проекту за допомогою функціональної
декомпозиції. Такі мови, як Pascal і C, є імперативними. У них порядок вихідних
рядків коду визначає порядок передачі управління, включаючи послідовне виконання,
вибір умов і повторне виконання ділянок
програми. Кожен модуль має декілька точок входу
і декілька точок виходу.
Складні програмні проекти мають модульно-ієрархічну побудову і тестування модулів є початковим кроком процесу
тестування ПЗ. Побудова графу моделі модуля є
тривіальним завданням, а тестування практично завжди проводиться за критерієм
покриття гілок C1, тобто кожна дуга і кожна вершина
графа модуля повинні міститися, принаймні, в одній з шляхів тестування.
Таким чином, M(P,C1)=
E Nij,
де Е — безліч дуг, а Nij —
вхідні вершини ГМП.
Складність тестування модуля за критерієм С1 виражається уточненою формулою для оцінки
топологічної складності Мак-Кейба:
V(P,C1)= q + kin, де q — число бінарних виборів для умов розгалуження, а kin —
число входів графу.
Для інтеграційного тестування найбільшим
Під час складання
модулів в єдиний програмний комплекс з’являється два варіанти побудови моделі графу проекту:
Ø Плоска або ієрархічна
модель проекту.
Якщо програма P складається
з p модулів, то під час інтеграції модулів в комплекс фактично виходить громіздка
плоска або простіша — ієрархічна (мал. 8) — модель програмного проекту. Як критерій тестування на інтеграційному
рівні зазвичай використовується критерій покриття гілок C1.
Введемо також наступні позначення:
n — число вузлів в графі; |
e — число дуг в графі; |
q — число бінарних виборів з умов розгалуження в графі; |
kin — число входів в граф; |
kout — число виходів з графів; |
kext — число точок входу, які можуть бути викликані ззовні. |
Тоді складність інтеграційного тестування всієї програми P за
критерієм C1 може бути виражена формулою:
Проте при подібному підході до побудови ГМП (граф
моделі програми) розробник тестового набору стикається з неприйнятно високою
складністю тестування V (P, C) для проектів
середнього і великого об’єму (розміром в 105 — 107 рядків), що виходить із зростання топологічної складності
керуючого графа, за Мак-Кейбу. Таким чином,
використовуючи плоску або ієрархічну модель, важко дати оцінку протестованості TV(P, C, T) для всього проекту і оцінку залежності
протестованості проекту від протестованості окремого модуля TV(Modi, C), включеного в цей проект.
Розглянемо другу модель збірки модулів в процедурному програмуванні —
граф викликів. У цій моделі в разі інтеграційного тестування враховуються лише виклики
модулів у програмі. Тому з безлічі M (Modi, C)
тестованих елементів можна виключити ті елементи, які не схильні до впливу
інтеграції, тобто вузли і дуги, не сполучені з викликами модулів:
де
або nj містить
виклики модулів, тобто
E — підмножина ребер графа модуля, а Nin — "вхідні" вузли графа.
Ця модифікація ГМП наводить до здобуття нового графу — графу викликів,
кожен вузол в цьому графові представляє модуль (процедуру), а кожна дуга —
виклик модуля (процедури). Для процедурного програмування подібний
крок спрощує графову модель програмного проекту до прийнятного рівня
складності. Таким чином, може бути визначена цикломатична складність спрощеного
графа модуля Modi як V’(Modi, C’), а формула, що виражає складність
інтеграційного тестування програмного проекту,
набирає наступного вигляду
Так, для програми, ГМП, яка приведена на мал.8 для здобуття графа викликів з ієрархічної
моделі проекту мають бути виключені всі дуги, окрім:
1.
Дуги 1—2, що містить вхідний вузол 1 графа G.
2.
Дуг 2—8, 8—7, 7—10, що містять виклик модуля G1.
3.
Дуг 2—9, 9—7, 7—10, що містять виклик модуля G2.
У результаті граф викликів набере
вигляду, показаного на Мал.9, а складність даного графа
за критерієм C1’ рівна:
V’(G,C1’)= q + Kext =1+1=2.
V’(Modi, C’) також називається в літературі складністю модульного
дизайну (complexity of module design).
Мал. 9. Граф викликів модулів
Питання для
самоконтролю:
1.
Пояснити
значення модульного тестування (Unit
Testing) для розробки програмних продуктів, навести приклади і
умови його використання.
2.
Пояснити
значення інтеграційного тестування, навести приклади і умови його використання.
3. Описати основні етапи
тестування зручності і простоти використання (Usability testing), обґрунтувати доцільність
проведення такого тестування.
4. Розробка, керована тестуванням (Test-driven development).