Пролог

Усміхаєшся, коли чуєш “інтеграційні тести”? Ну звісно: знову треба піднімати десяток сервісів у Docker, а лептоп при цьому перетворюється на обігрівач, усе гальмує, шумить і гріється.
А що як я скажу, що є альтернатива Docker? 🤔
Ще у 2024 році Josh Long презентував Spring Boot Testjars — експериментальну фічу, яка дозволяє запускати Spring Boot-додатки прямо з Java-тестів. Без Docker. Круто? Ще б пак! Але не все так ідеально — Testjars ще в експериментальній фазі, не вистачає багатьох речей, але основне вже працює, і це відкриває цікаві можливості.
Епіграф
Я, натхненний цією ідеєю, написав невеличкий pet-проєкт, щоб показати, як Testjars працює на практиці.
Маю два сервіси:
📚
bookshelf
— простий CRUD-додаток, який буде запускатись як окремий jar у тестах;🔁
demotestjars
— сервіс-проксі до bookshelf, який використовує Feign-клієнт для взаємодії.
Як це працює
Спершу нам треба зібрати bookshelf як .jar
, придатний для запуску з тестів. Просто зарань mvn clean install
😊
Крок 1: Додаємо залежності
<dependency>
<groupId>org.springframework.experimental.boot</groupId>
<artifactId>spring-boot-testjars</artifactId>
<version>0.0.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.experimental.boot</groupId>
<artifactId>spring-boot-testjars-maven</artifactId>
<version>0.0.4</version>
</dependency>
Крок 2: Створюємо проперті файл для bookshelf
server.port=18083
spring.datasource.url=jdbc:h2:tcp://localhost:9092/mem:bookshelf
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
💡 Ми використовуємо H2 замість реальної БД — якраз реальний кейс з життя
Крок 3: Конфігуруємо запуск bookshelf у тестах
@Bean
static CommonsExecWebServerFactoryBean bookShelfServer() {
return CommonsExecWebServerFactoryBean.builder()
.useRandomPort(false)
.classpath(cp -> cp
.entries(
new MavenClasspathEntry("com.example:bookshelf:0.0.1-SNAPSHOT"),
new MavenClasspathEntry("com.h2database:h2:2.3.232"),
new ResourceClasspathEntry("book-shelf.properties", "application.properties")
)
);
}
І все! Тепер bookshelf
запускається як окремий Spring Boot сервіс просто у вашому інтеграційному тесті — без Docker і YAML’ів.
Що бачимо у логах
Коли запускаєш тести, бачиш логи не лише основного застосунку (demotestjars
), а й запущеного bookshelf
— як у справжньому інтеграційному середовищі. Жодного Docker.
2025-05-11T23:26:05.517+03:00 INFO 4312 --- [demotestjars] [o-auto-1-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-05-11T23:26:05.518+03:00 INFO 4312 --- [demotestjars] [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-05-11T23:26:05.518+03:00 INFO 4312 --- [demotestjars] [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
2025-05-11T23:26:05.556+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] ---> POST http://localhost:18083/api/books HTTP/1.1
2025-05-11T23:26:05.556+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] Content-Length: 78
2025-05-11T23:26:05.556+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] Content-Type: application/json
2025-05-11T23:26:05.556+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook]
2025-05-11T23:26:05.556+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] {"id":null,"title":"Test Driven Development","author":"Kent Beck","year":2003}
2025-05-11T23:26:05.556+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] ---> END HTTP (78-byte body)
2025-05-11T23:26:05.589+03:00 INFO 4316 --- [bookshelf] [io-18083-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-05-11T23:26:05.590+03:00 INFO 4316 --- [bookshelf] [io-18083-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-05-11T23:26:05.590+03:00 INFO 4316 --- [bookshelf] [io-18083-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
2025-05-11T23:26:05.655+03:00 INFO 4316 --- [bookshelf] [io-18083-exec-1] c.example.bookshelf.service.BookService : save Book: Book(id=null, title=Test Driven Development, author=Kent Beck, year=2003)
Hibernate: insert into book (author,title,created_year,id) values (?,?,?,default)
2025-05-11T23:26:05.736+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] <--- HTTP/1.1 200 (179ms)
2025-05-11T23:26:05.736+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] connection: keep-alive
2025-05-11T23:26:05.736+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] content-type: application/json
2025-05-11T23:26:05.736+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] date: Sun, 11 May 2025 20:26:05 GMT
2025-05-11T23:26:05.736+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] keep-alive: timeout=60
2025-05-11T23:26:05.736+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] transfer-encoding: chunked
2025-05-11T23:26:05.736+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook]
2025-05-11T23:26:05.737+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] {"id":1,"title":"Test Driven Development","author":"Kent Beck","year":2003}
2025-05-11T23:26:05.737+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-1] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#addBook] <--- END HTTP (75-byte body)
2025-05-11T23:26:05.782+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] ---> GET http://localhost:18083/api/books HTTP/1.1
2025-05-11T23:26:05.782+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] ---> END HTTP (0-byte body)
2025-05-11T23:26:05.784+03:00 INFO 4316 --- [bookshelf] [io-18083-exec-2] c.example.bookshelf.service.BookService : get All Books
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.created_year from book b1_0
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] <--- HTTP/1.1 200 (67ms)
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] connection: keep-alive
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] content-type: application/json
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] date: Sun, 11 May 2025 20:26:05 GMT
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] keep-alive: timeout=60
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] transfer-encoding: chunked
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks]
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] [{"id":1,"title":"Test Driven Development","author":"Kent Beck","year":2003}]
2025-05-11T23:26:05.850+03:00 DEBUG 4312 --- [demotestjars] [o-auto-1-exec-2] c.d.demotestjars.client.BookShelfClient : [BookShelfClient#getAllBooks] <--- END HTTP (77-byte body)
2025-05-11T23:26:05.903+03:00 INFO 4312 --- [demotestjars] [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2025-05-11T23:26:05.905+03:00 INFO 4312 --- [demotestjars] [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
2025-05-11T23:26:05.906+03:00 INFO 4312 --- [demotestjars] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-05-11T23:26:05.911+03:00 INFO 4312 --- [demotestjars] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Мінуси (поки що)
Testjars має потенціал, але поки не все працює як хочеться:
1. Не працює анотація @DynamicProperty
Ця фіча також є в документації, але в реальному житті вона поки що не працює. Подробиці:
https://github.com/spring-projects-experimental/spring-boot-testjars?tab=readme-ov-file#dynamicproperty
Епілог
Чи зможе Testjars замінити Docker? Повноцінно — ні. Адже Postgres, Oracle чи Redis у вигляді jar-файлів ти не запустиш. Але якщо тобі треба інтегрувати лише власні мікросервіси — це класне рішення.
Особливо, якщо ти працюєш на віртуалці або слабкому ноуті, де Docker — це біль.
Testjars ще сирий, але вже зараз економить купу нервів. А в майбутньому — можливо, стане стандартом.
🔗 Посилання на репозиторії: