Важливо мати можливість проводити деякі інтеграційні тестування без обов'язкового розгортання на сервері програм або підключення до іншої корпоративної інфраструктури. Це дозволить тестувати такі речі як:

  • Правильне підключення контекстів IoC-контейнера Spring.

  • Доступ до даних за допомогою JDBC або ORM-інструменту. Це може включати, зокрема, коректність SQL-інструкцій, запитів Hibernate, відображень сутностей JPA тощо.

Spring Framework надає першокласну підтримку інтеграційного тестування в модулі spring-test. Ім'я фактичного JAR-файлу може включати версію випуску, а також може бути представлене у довгій формі org.springframework.test, залежно від того, звідки ти його отримуєш (звернися до розділу про керування залежностями за поясненнями). Ця бібліотека включає пакет org.springframework.test, який містить корисні класи для інтеграційного тестування за участю контейнера Spring. Таке тестування не залежить від сервера програм або іншого середовища розгортання. Такі тести виконуються повільніше за модульні, але набагато швидше, ніж еквівалентні тести Selenium або віддалені тести, які покладаються на розгортання на сервері додатків.

Підтримка модульного та інтеграційного тестування представлена у вигляді анотаційно-орієнтованого фреймворку Spring TestContext Framework. Фреймворк TestContext не залежить від реально використовуваної інфраструктури тестування, що дозволяє інструментувати тести в різних середовищах, включно з JUnit, TestNG та інші.

Мета інтеграційного тестування

Підтримка інтеграційного тестування в Spring має такі основні цілі:

  • Керування кешуванням IoC-контейнера Spring між тестами.

  • Забезпечення впровадження залежностей для екземплярів тестового стенду (середовища).

  • Забезпечення, що відповідає інтеграційному тестуванню.

  • Надання специфічних для Spring основних класів, які допомагають розробникам у написанні інтеграційних тестів.

У наступних кількох розділах описується кожна мета і наводяться посилання на детальні пояснення щодо реалізації та конфігурування.

Керування контекстом та кешування

Spring TestContext Framework забезпечує послідовне завантаження екземплярів ApplicationContext та WebApplicationContext зі Spring, а також кешування цих контекстів. Підтримка кешування завантажених контекстів важлива, оскільки час початкового запуску може стати проблемою — не через затримку в самому Spring, а через те, що об'єктам, екземпляри яких створюються контейнером Spring, потрібен час створення. Наприклад, у проєкті з 50-100 файлами відображення Hibernate може знадобитися від 10 до 20 секунд для завантаження файлів відображення, і ці витрати перед запуском кожного тесту в кожному тестовому стенді призводять до уповільнення загального тестування, що знижує продуктивність розробників.

Тестові класи зазвичай оголошують або масив розташування ресурсів для метаданих конфігурації XML або Groovy — часто в шляху класів — або масив компонентних класів, який використовується для конфігурування програми. Ці розташування або класи є такими ж або схожими на ті, які вказані в web.xml або в інших конфігураційних файлах для розгортання у виробничому середовищі.

За замовчуванням після завантаження налаштований ApplicationContext використовується повторно для кожного тесту. Таким чином, ресурси на налаштування витрачаються лише один раз для кожного тестового комплекту, а подальше виконання тестів відбувається набагато швидше. У цьому контексті термін "тестовий комплект" означає всі тести, що виконуються в одній JVM — наприклад, всі тести, що виконуються зі складання Ant, Maven або Gradle для цього проекту або модуля. У малоймовірному випадку, якщо тест пошкоджує контекст програми та вимагає перезавантаження (наприклад, при зміні визначення біну або стану об'єкта програми), фреймворк TestContext можна налаштувати на перезавантаження конфігурації та відновлення контексту програми перед виконанням наступного тесту.

Використання залежностей для тестових стендів

Коли фреймворк TestContext завантажує контекст програми, він може опціонально налаштовувати екземпляри тестових класів за допомогою впровадження залежностей. Це дозволяє забезпечити зручний механізм для налаштування тестових стендів за допомогою попередньо налаштованих бінів з контексту програми. Великою перевагою тут є можливість повторного використання контекстів додатків у різних сценаріях тестування (наприклад, для налаштування графів об'єктів, керованих Spring, транзакційних проксі, екземплярів DataSource та ін.), що позбавляє необхідності дублювати складне налаштування тестових стендів для окремих тестових випадків.

Як приклад розглянемо сценарій, у якому ми маємо клас (HibernateTitleRepository), що реалізує логіку доступу до даних для сутності предметної області Title. Ми хочемо написати інтеграційні тести, які тестують такі сфери:

  • Конфігурація Spring: головним чином, чи коректно подано і чи є все необхідне, що пов'язано зі зміною біна HibernateTitleRepository?

  • Конфігурація файлу відображення Hibernate: чи правильно відображено і чи встановлені правильні налаштування відкладеного завантаження?

  • Логіка репозиторію HibernateTitleRepository: чи виконує налаштований екземпляр цього класу очікувані функції?

Керування транзакціями

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

Фреймворк TestContext вирішує цю проблему. За замовчуванням фреймворк створює та відкочує транзакцію для кожного тесту. Можна написати код, здатний припускати існування транзакції. Якщо ти викликаєш проксовані об'єкти у своїх тестах транзакційно, вони поводяться правильно, відповідно до налаштованої транзакційної семантики. До того ж, якщо тестовий метод видаляє вміст вибраних таблиць під час виконання керованої для тесту транзакції, ця транзакція відкочується за замовчуванням, а база даних повертається до стану, що передував виконанню тесту. Підтримка транзакцій надається тесту за допомогою біна PlatformTransactionManager, визначеного в контексті програми тесту.

Якщо тобі потрібно, щоб транзакція була зафіксована (нестандартно, але іноді корисно, якщо потрібно, щоб певний тест заповнив або змінив базу даних), то можна вказати фреймворку TestContext викликати фіксацію транзакції замість відкату, використовуючи інструкцію @Commit.

Див. керування транзакціями за допомогою фреймворку TestContext (див. посилання наприкінці лекції).

Допоміжні класи для інтеграційного тестування

Spring TestContext Framework надає кілька абстрактних допоміжних класів, які спрощують написання інтеграційних тестів. Ці основні тестові класи надають чітко визначені перехоплювачі, що підключаються до інфраструктури тестування, а також допоміжні змінні екземпляри та методи, які дозволяють отримати доступ до:

  • ApplicationContext — для виконання явного пошуку бінів або тестування стану контексту загалом.

  • JdbcTemplate — для виконання SQL-інструкцій для запитів до бази даних. Можна використовувати такі запити для підтвердження стану бази даних як до, так і після виконання коду програми, пов'язаного з базою даних, і Spring забезпечить, що такі запити виконуватимуться в рамках тієї ж транзакції, як і код програми. При використанні в поєднанні з інструментом ORM слід уникати помилкових спрацьовувань (див. посилання в кінці лекції).

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

Див. все про фреймворк TestContext та його взаємодію з: "Управлінням/кешуванням контексту", допоміжними класами для фреймворку, а також впровадженням залежності до тестових стендів та керуванням транзакціями за його допомогою.