Data Driven Testing (DDT) - підхід до створення / архітектурі автоматизованих тестів (юніт, інтеграційних, найчастіше можна застосувати до backend тестування), при якому тест вміє приймати набір вхідних параметрів, і еталоннийрезультат або еталонний стан. з яким він повинен порівняти результат, отриманий в ході прогонки вхідних параметрів. Таке порівняння і є assert такого тесту. Притому як частина вхідних параметрів, можуть передаватися опції виконання тесту, або прапори, які впливають на його логіку.
Часто покривається система, або метод є дуже складними, і тоді неможливо ввести явно еталонні значення, тут можна говорити про еталонних вихідних станах. Буває, потрібно застосувати кмітливість, щоб зрозуміти, що ж буде вхідним описом, а що буде вихідним. Наприклад:
- Частиною вхідного опису може бути стан бази даних. тобто в функціонал DDT буде входити setUp бази даних. Цей стан можна брати з SQL дампа, а можна генерувати програмно на java (другий спосіб легше підтримувати). Дивіться приклади 4 і 6.
- Частиною еталонного вихідного стану знову-таки може бути стан бази даних. Але буває, що все набагато складніше, і нам потрібно перевірити не просто стан, а послідовність викликів, івентів, і навіть, контекст після кожного дзвінка всередині системи, тоді хорошою ідеєю може бути побудова дерева викликів під час прогону тесту. тобто логирование потрібного в потрібному форматі (наприклад, XML або JSON), і тоді еталонними даними буде перевірене раніше дерево викликів. До речі, еталонне дерево викликів можна записати в першій прогоні, потім вручну перевірити. що воно - правильне, і далі - вже його використовувати для тестів. Дивіться приклади 3 і 7.
переваги використання
Або ж, творець такого тесту може дати колегам workflow, за яким можна просто підготувати еталонні значення. Тобто бажано уникнути залежності в створенні наборів даних від програмістів, і автоматизаторів, будь на проекті повинен зуміти їх підготувати.
Також, керівнику проекту стає легше контролювати розробку, йому не доводиться вникати в суть алгоритмів, і навіть якості зробленого коду, DDT тестів цілком достатньо для просування вперед.
Використання DDT, як не дивно, дозволяє використовувати на проектах менш кваліфікованих інженерів, так як хороше покриття складних ділянок за допомогою DDT відразу ж все показує, і людина за рахунок Continuous Integration і прогону тестів локально або на Dev оточенні, має можливість самостійно фіксують внесені проблеми .
Зазначу, що при певному підході у використанні, Robot Framework тести можуть бути дуже DDT-орієнтованими, і давати масу вигод.
Найпростіша форма DDT - параметризрвані тести в JUnit, коли за допомогою Data Providers в тест поставляються набори даних для тестування.
DDT - не панацея, але грамотне застосування в потрібних місцях допомагає дуже і дуже.
Як зрозуміти, коли потрібно створювати DDT
Як створювати DDT
Найчастіше, досить зробити цикл над основним тестом, і організувати порівняння вихідних даних і еталонних, а також реалізувати Репортинг - шляхом логгірованія, або іншим способом.
Але в нетривіальних випадках, можлива побудова архітектури навколо необхідності створити DDT. Дивіться приклади 3 і 7.
Завдання. Переклад числа в цифровому записі в строкову. Наприклад 134345 буде «сто тридцять чотири тисячі триста сорок п'ять». * Врахувати відміни - різниця в закінченнях (наприклад, дві і два).
- Алгоритм повинен працювати для скільки завгодно великого числа, відповідно, значення розрядів - мільйон, тисяча, мільярд і т.д. - повинні братися з довідника, наприклад, текстового файлу.
- Обов'язково створити Data Driven Test (я, як користувач, повинен мати можливість ввести безліч наборів 1.чісло 2.правільний еталоннийрезультат, тест самостійно перевіряє всі набори і каже, що невірне), який доводить, що Ваш алгоритм працює правильно. Використовувати JUnit.
- По можливості, застосувати ООП.
Завдання. Реалізувати HTML парсер з нуля. Тобто реалізувати новий рядок в DOM модель.
Один інженер буде йти таким шляхом. Він попросить час, щоб вичитати специфікацію. Потім попросить подумати над архітектурою. Потім буде робити прототипи. Якось їх тестувати. Весь цей час, дні, тижні, менеджер і команда не зможуть перевіряти його статус на ділі.
Другий інженер в перший же день зробить перший unit test, який буде перевіряти найпростіший випадок - порожній рядок, або ж, порожній тег
, або щось ще просте. І кожен день він буде накидати нові стану, розширюючи свій код. Логічно це буде обернути в DDT, і дозволити всій команді, менеджерам і тестувальникам накидати різні варіанти HTML, а також еталонного результату (наприклад, дерево DOM об'єктів, які можуть записуватися першим прогоном цього алгоритму і верифікувати вручну). У такому підході накопичуються кейси, зміни в алгоритмі і логіці не валять попередні набори даних, і з'являється можливість чітко розуміти, що саме вже реалізовано. Більш того, набори вхідних даних і еталонні значення можна розбити на папки, підпапки, і таким чином створити документацію парсеру.3. Підхід, який застосовується в тестуванні платформи для автоматизації тестування XML2Selenium
XML2Selenium - система, побудована на плагінах і на взаємодії плагінів. Модулі генерують івенти, підписуються на івенти інших плагінів. Тобто від користувача приховано складну взаємодію.
XML2Selenium вміє прокрутити тести, записані в форматі XML, і тестувати Web UI додаток (всередині ми використовуємо Selenium / Web Driver).
Природно, що будь-яка зміна в ядрі системи може поламати логіку взаємодії, вбити зворотну сумісність (при появі нових полігонів), більш того, ядро системи вимагало роботи Senior-інженерів, ціна помилки була висока.
Ми застосували такий підхід. Був введений спеціальний JVM параметр, який просив ядро зберігати все дерево викликів і івентів для даного тесту, включаючи реєстрацію, ініціалізацію, і весь життєвий цикл плагінів, а також контекст, що передається між плагінами після кожного атомарного дії тесту. Таким чином, ми отримували сгенерённий файл, який містив повний зліпок поведінки системи, ось приклад такого файлу:
Після того, як розробник або тестувальник вручну верифікувати, що дане дерево для даного тесту - коректно, воно відправлялося в набір еталонних значень, і таким чином, на Continuous Integration сервері Jenkins майстер гілки для кожного запущеного тесту порівнювався дерево розбору, що вкрай стабілізувало стан системи .
4. Підхід, який застосовувався на одному з проектів до тестування вивантаження даних
У цьому випадку на проекті було потрібно протестувати, наскільки правильно працював компонент вивантаження даних. Вивантаження була нетривіальна, залежала від стану бази даних, і в ній раз у раз спливали баги. Можна було сказати, що команда не контролювала стабільність цього компонента. Було потрібно радикально збільшити його стабільність.
Рішення було у використанні DDT. Вхідним параметром виступав дамп бази даних в форматі XML. Еталонним значенням - раніше перевірений файл вивантаження, свідомо безпомилковий. Таким чином, тестування звелося до створення різних версій бази даних, а також до перевірки еталонних файлів, і створення таких тестових наборів.
На проекті була забезпечена достатня регресійний. Кожен раз, коли з'являвся баг в вивантаженні - додавався ще один набір даних, який покривав цю ситуацію. Набори накопичувалися, їх існування не дозволяло вносити нові баги, і через якийсь час компонент стабілізувався.
Наскільки я пам'ятаю, ми також реалізували генерацію потрібного стану бази даних з Java, щоб не залежати від дампов, і не оновлювати їх постійно зі змінами схеми даних.
5. Реальний проект - тестування обробки помилок у складній граматиці
Суть проекту була наступною. На клієнтську частину від сервера приходив XML з повним описом не тільки UI, який потрібно було отрисовать, але і з поведінкою, яке передавалося в бінарному форматі у цьому ж XML. Тобто поведінка могла бути яким завгодно, в наявності плагінів система.
Даний XML мав досить складний синтаксис, і наша команда була незалежна від розробників backend. Нам необхідно було убезпечити себе від неправильних XML, ми повинні були починати побудову UI тільки тоді, коли 100% розуміли, що XML правильний.
Для цього був використаний DDT. Ми створили величезну кількість варіантів вхідного XML, в тому числі, неправильних, некоректних, і перевіряли, що викидаються потрібні виключення з потрібними повідомленнями.
Таким чином, вхідний параметр - це XML, еталонний стан - тип виключення, його повідомлення, або навіть, частина stack trace. Було створено кілька сотень тестових наборів, і кожен раз, коли змінювалося щось в форматі, з'являлися нові параметри, або ж, з'являлися нові винятки і баги - тести розширювалися. Під закінчення проекту це була найстабільніша частина системи.
На цьому зображенні наведена частина Excel файлу, в якому задається вхідний параметр і еталонне поведінку - в нашому випадку, інформація про винятки (тип і повідомлення).
6. Тестування складної завантаження даних. Ізолювання логіки збережених процедур
На одному з проектів відбувається завантаження даних в форматі CSV (дуже складного внутрішнього формату) в базу даних. Притому нам дістався legacy код, і вся логіка завантаження відбувається в збережених процедурах, в яких близько десятка тисяч рядків. Завдання полягало в тому, щоб стабілізувати цей компонент вивантаження, і забезпечити регресійне тестування.
Як і в прикладі 4. з вивантаженням даних, ми застосували DDT, і в якості вхідних параметрів використовували 1) стан бази даних (ми використовували дамп в форматі SQL) і 2) файл, який потрібно буде завантажити. В якості еталонного значення ми використовували XLS файл, який представляв вміст потрібних нам таблиць цієї бази даних після завантаження.
Всі баги, які часто траплялися, ми поміщали в DDT.
Незабаром, ми виявили, що наші фікси не впливають на минулі баги, і що багів стало менше, а контроль над ними - став вище, так як зараз є можливість швидко і ефективно їх відтворювати автоматично, додаючи нові набори.
У разі цього проекту, зважаючи на відсутність часу, використовувалися сирі дампи, відповідно, потенційно підтримка такої системи в подальшому буде вимагати витрат.
При створенні SOA платформи (тобто фреймворка для SOA проектів) була поставлена задача тестування поведінки системи, системної шини, відтворення всіх можливих ситуацій, які можуть статися під час життєвого циклу SOA проекту.
Для реалізації цього завдання був створений фреймворк, що дозволяє за описом системи розгортати її - в реальності це різні віртуальні сервера, які пов'язані один з одним через нашу SOA платформу. На кожному з серверів було розгорнуто додаток, яке було підключено до системної шини, і вміло виконувати скрипти, які до нього надходили. У переданих скриптах ми зашивали необхідну поведінку і необхідний сценарій.
Таким чином, вхідними даними були 1) файл опису топології системи і 2) набір скриптів для кожного вузла цієї топології, які говорили, що відбувається на кожному з вузлів, а еталонними значеннями було дерево (своєрідний лог) обробки повідомлень в такій системі.