Резюме: хоч Maven і є інструментом, стандартизує структуру проекту і його збірку, проте він з крахом провалив стандартизацію різного роду тестів. Ща розберемо польоти.
Отже, чому ж все-таки Maven не справляється зі стандартизацією тестів, адже:
- Виконуються на фазі test
- Лежать в src / test каталозі
- Назви тестових класів містять слово Test
-
- Виполнаются на фазі integration-test
- Лежать в src / test
- В їх імені повинні бути літери IT позначають собсно IntegrationTests.
Проблем насправді з цим кілька:
Фаза integration-test виконується після модульних тестів, це означає що окремо їх не запустити. Кожен раз коли ми запускаємо інтеграційні тести, виконуються модульні. Однак зазвичай ми хочемо запустити модульні тести один раз, а потім окремо запускати інтеграційні. Виходить щоб модульні тести не виконувалися нам потрібно пропускати їх за допомогою -DskipTests. потім виявиться що інтеграційні тести теж не запускаються бо failsafe плагін використовує під собою surefire, почнеться геморой зі створенням профілів і врешті-решт почне здаватися що все це занадто складно. До речі, чому нам важливо запускати тести окремо:
Розробники можуть швидко отримати "зелений" фідбек і продовжити працювати. Саме модульні тести здатні швидко дати базовий відповідь.
Немодульность тести залежать від оточення, а значить можуть падати. Якщо впали такі тести, то ще потрібно розібратися з-за оточення чи, або й справді зламалася логіка.
Інтеграційні тести повільні, в залежності від тривалості їх можна запускати по кожному коммітов, раз в день і т.п. Вони теж можуть дробитися на більш дрібні групи тестів, наприклад, основні Smoke тести виконуються першими, потім регресія, потім приймальні, потім якісь навантажувальні і т.п. Їх зручно розділяти бо ми точно будемо знати що зламалося. А також ми можемо запускати окремо певну групу тестів і не чекати 4 години щоб пройшло все.
Ми можемо хотіти запускати одні і ті ж системні кілька разів. Наприклад, ми їх прогнали за поточним коммітов, а потім ще по тому що був тиждень назад для того щоб побачити різницю між результатами.
Для системних тестів часто потрібно підготувати оточення перш ніж їх запускати.
Стандартно failsafe використовує каталог src / test. а нам рідко коли потрібно поміщати інтеграційні і модульні тести і ресурси в одні і ті ж пакети.
Фаза integration-test запускається перед install кожен раз. Ми ж не хочемо запускати повільні інтеграційні тести кожен раз коли встановлюємо артефакти в локальний репозиторій.
На жаль для більшості розробників існують лише модульні, інтеграційні тести і "те, що роблять QA". Однак насправді тести поділяються як мінімум на модульні, системні та компонентні в залежності від масштабів. Також є функціональні і нефункціональні тести і т.п. Більш докладно можна з видами тестування ознайомитися в однойменній статті. Але що головне - ми можемо захотіти розділити всі ці тести. Для одних потрібно піднімати все додаток, для інших - лише частина, для третіх взагалі одного класу вистачить. Однак Maven їх ніяк не розрізняє і не поділяє, у нього є або модульні, які інтеграційні.
Загалом подумавши трохи можна прити до висновку, що раз стандартний механізм Maven настільки недосконалий і все одно не зможе підтримувати лише потрібного, ми можемо відійти від нього. Замість цього пропоную використовувати plain old surefire plugin. Так, цей плагін заточений на написання модульних тестів, проте вони за фактом нічим не відрізнятимуться від "немодульность" - ті ж JUnit / TestNG будуть описувати всю їхню логіку (хоча тут дозволяється також використовувати всякі BDD фреймворки навроде JBehave. Проте не про них мова ).
Так ось як же це буде виглядати. Для кожного з видів тестування ми будемо створювати а) профіль б) каталог з вихідними кодами і ресурсами. Конфигурироваться ж Maven буде наступним чином:
Зауважте, що у відносно невеликому шматку конфігурації ми описали 3 види тестів і всі вони знаходяться в різних каталогах:
Модульні: mvn test
Компонентні: mvn test -Pcomponent-test
Системні: mvn test -Psystem-test
Структура каталогів тоді така:
Правда системні тести як правило має сенс виділяти в окремі модулі, ато і проекти.
Єдине в чому я злукавив: фаза інтеграційних тестів ділитися на кілька кроків і це дозволяє нам спочатку підняти оточення, а потім його зупинити. Підхід з surefire такого не дасть, вам доведеться вручну запускати оточення за допомогою явно зазначених команд. Однак:
Якщо нам потрібно повністю розгорнуте оточення, то це не завжди можливо автоматизувати без написання скриптів, що означає що в більшості випадків все одно доведеться руками смикати їх
Складність в конфігурації різного роду тестів перекриває складність запуску якого-небудь tomcat: start
Оскільки зазвичай системні тести виносять в окремі модулі або проекти, то там цілком можна використовувати фазу integration-test як єдину, яка запускає тести.
Але якщо вам справді хочеться зручним чином автоматизувати розгортання оточення, можливо вам буде краще конфігурувати integration-test фазу для цього.
JUnit Categories, TestNG Groups
Проблема виявлена правильно але вирішена не тими засобами, профілі тут використовувати помилково тому як:
Тепер не можна в збірці запустити одночасно і юніт і інтеграційні тести, а між іншим такий сценарій збірки є самим распространненость, збірка на білд сервері повинна здійснюватися прогоном всіх тестів, та й причина по якій ми можемо захотіти запустити інтеграційні тести не запускаючи юніт тести може бути тільки одна: в коді немає проблем, інтеграційні тести не виконалися бо ми підготували поганий тестовий набір, например не накотили апдейт схеми на базу, або підготували неправильні датасета для dbunit. А ось якщо інтеграційні тести знайшли реальну помилку в коді то їх вже не в якому разі не можна запускати без юніт тестів бо, по-перше у нас велика проблема в юніт тестах, вони неповні тому як прогавили помилку, друге сама процедура виправлення помилки при agile така що спочатку потрібно написати юніт тест виявляє факт того що помилка є а вже потім вносити виправлення в код. Ситуація коли ми хочемо запустити юніт тесту не запускаючи інтеграційних це так, вона реальна і я покажу нижче як її обійти.
Незрозуміло як це все буде виглядати в IDE, при запропонованому підході у нас буде одночасно під контролем компілятора і інших валідаторів тільки одна папка з тестами, припустимо правимо код і юніт тести і не бачимо що код інтеграційних тестів більше не компілюється.
Тепер як я обино розношу юніт і інтеграційні тести по різних фаз:
Інтеграційні тести просто кладемо в паект integration і вони не сіпаються на фазі юніт тестування, як власне і на оборот. Як видно по дефолту юніт тести запускаються завжди, що начебто логічно, але ми можемо їх пропустити виставивши property skip.unittest в true якщо нам треба.
P.S. Як видно було використано кунгфу по запуску одне і того ж плагіна на декількох фазах, таким чином неважко здогадатися що ми можемо мати не тільки дві але навіть більше фаз тестування в своєму проекті, наприклад юніт, інтеграційні, функціональні, приймальні просто сконфігурованої плагін на запуск в різних фазах.
Якщо все таки хочеться вирішувати проблему саме через профілі і ніяк інакше, наприклад конкретно у вашому проекті з якихось причин це обґрунтовано, то вигідніше маніпулювати елементом 'includes' замість елементів 'testResources' і 'testSourceDirectory', так як перед нами спочатку стоїть проблема які тести запускати, елемент includes 'дає прямий і однозначну відповідь на поставлене запитання не заважаючи при використанні IDE компілятору перевіряти код тестів, а валідатор ресурсів перевіряти тестові ресурси.