یک فیلد خالی librarianID در فرم امانت، بهجای null بهصورت رشته خالی ذخیره میشد و بررسی ذخیرهشدن اطلاعات را در صفحهای دیگر بههم میزد. این bug حاصل تعامل اتصال مقدار در JSF با callback مربوط به @PrePersist بود؛ بنابراین آزمایش EntityManager با Mock آن را آشکار نمیکرد. مشکل زمانی ظاهر میشد که مرورگر واقعی، فرم واقعی را به برنامه deployشده میفرستاد.
مطلب با این مثال، کاربرد Integration Test با Testcontainers را توضیح میدهد: اجرای همان WAR ساختهشده با Maven روی یک نمونه واقعی Azul Payara Micro و آزمایش آن از بیرون. Testcontainers در طول اجرای آزمایش، container موقت میسازد و پس از پایان آن را حذف میکند. آزمایش از طریق شبکه و port واقعی با برنامه ارتباط میگیرد. Ryuk نیز برای پاکسازی containerها حتی در صورت توقف JVM به کار میرود، هرچند ممکن است در بعضی محیطهای CI غیرفعال باشد.
در پروژه نمونه، کلاس PayaraMicroContainer از GenericContainer ارث میبرد، docker image مربوط به payara/micro را با نسخه مورد استفاده پروژه دریافت میکند و WAR تازه ساختهشده را داخل آن کپی میکند. Wait Strategy بر پیام آمادهشدن خود Payara تکیه دارد. ویژگیهای payara.version و war.path را Maven Failsafe فراهم میکند؛ نبودن آنها باعث خطای صریح میشود. همچنین war.path به خروجی مرحله package اشاره دارد تا نسخه قدیمی WAR آزمایش نشود.
نکته دیگر، استفاده از Singleton Pattern برای container مشترک است. پروژه آن را در static initializer کلاس پایه AbstractContainerIT راهاندازی میکند. دلیل این انتخاب، چرخه عمر افزونه JUnit 5 است: اگر فیلد static دارای @Container در کلاس پایه مشترک قرار بگیرد، پایان اولین کلاس آزمایش میتواند container را متوقف کند و کلاسهای بعدی به container خاموش متصل شوند.
این روش مشکلات بستهبندی، dependency مفقود و رفتار مخصوص runtime را آشکار میکند، اما هزینه دارد: نیاز به محیط اجرای container، راهاندازی در مقیاس ثانیه و احتمال شکستهای ناشی از شبکه یا دیر آمادهشدن. همچنین نگاه آن بیرونی است؛ پاسخ HTTP و تعامل مرورگر را میآزماید، اما دسترسی مستقیم به وضعیت مدیریتشده داخل container نمیدهد. پروژه برای همین محدودیت، مسیر Arquillian را هم حفظ کرده است.




