@SpringBootTest وسياق الاختبار
@SpringBootTest وسياق الاختبار
تؤدي اختبارات الوحدة باستخدام JUnit 5 وMockito دورها جيدًا في اختبار المنطق المعزول، لكنك ستحتاج في نقطة ما إلى التحقق من أن حبوب Spring تتوصّل ببعضها بشكل صحيح، وأن خصائص التهيئة تُحمَّل، أو أن طلب HTTP حقيقيًا يصل إلى المُعالج المناسب. هنا يأتي دور @SpringBootTest: إذ يُشغّل سياق ApplicationContext كاملًا (أو جزئيًا) داخل اختبارك، مانحًا إياك نفس حاوية IoC التي تعمل بها تطبيقات الإنتاج.
ما الذي يفعله @SpringBootTest فعلًا
عند تزيين فئة اختبار بـ@SpringBootTest، تقوم البنية التحتية لاختبارات Spring Boot بما يلي:
- تحديد موقع فئة
@SpringBootApplicationالرئيسية بالصعود في شجرة الحزم انطلاقًا من فئة الاختبار. - بناء
ApplicationContextالكامل — بمسح المكونات، ومعالجة فئات@Configuration، وتشغيل التهيئة التلقائية، وربطapplication.properties/application.yml. - حقن الحبوب في فئة الاختبار عبر
@Autowired، تمامًا كأي حبة يديرها Spring.
@SpringBootTest السياق الكامل لكنه لا يستمع على أي منفذ ما لم تحدد webEnvironment. بالنسبة للاختبارات التي تحتاج فقط إلى استدعاء حبوب الخدمة أو المستودع مباشرةً، فإن الوضع الافتراضي (بلا خادم) أسرع وكافٍ تمامًا.
أبسط اختبار تكامل ممكن يبدو هكذا:
السمة webEnvironment
يقبل @SpringBootTest معامل webEnvironment الذي يتحكم في ما إذا كان خادم مُضمَّن سيُشغَّل وكيف:
MOCK(الافتراضي): يُحمِّلWebApplicationContextببيئة Servlet وهمية. لا منفذ حقيقي. يُستخدم معMockMvc.RANDOM_PORT: يُشغِّل الخادم المُضمَّن على منفذ عشوائي مجاني. مثالي لاختبارات HTTP الكاملة من البداية إلى النهاية باستخدامTestRestTemplateأوWebTestClient. يُحقَن المنفذ عبر@LocalServerPort.DEFINED_PORT: يبدأ على المنفذ المحدد فيapplication.properties(الافتراضي 8080). محفوف بالمخاطر في بيئة CI — فلو عمل اختباران متوازيان سيتعارضان.NONE: يُحمِّلApplicationContextبلا بيئة ويب على الإطلاق. مفيد لاختبار حبوب طبقة الخدمة في تطبيق غير ويب.
التخزين المؤقت للسياق — أهم ميزة لتحسين الأداء
يستغرق تهيئة سياق Spring وقتًا: مسح المكونات، والتهيئة التلقائية، وإنشاء تجمع اتصالات HikariCP، والتحقق من مخطط Hibernate — قد يضيف كل هذا من 2 إلى 5 ثوانٍ لكل سياق. لو أنشأت كل فئة اختبار سياقها الخاص، قد تنفق مجموعة مكونة من 200 اختبار وقتًا أطول في الإعداد من الأوقات التي تقضيها في التحقق الفعلي.
تحل بنية اختبارات Spring هذه المشكلة عبر التخزين المؤقت للسياق: يُشغَّل السياق مرة واحدة لكل مفتاح تهيئة فريد ويُعاد استخدامه من قِبل كل فئة اختبار تشترك في ذلك المفتاح. يُشتق مفتاح الذاكرة المؤقتة من:
- مجموعة تعليقات تهيئة السياق (
@SpringBootTest،@ContextConfiguration، إلخ). - قيمة
webEnvironment. - أي ملفات تعريف نشطة (
@ActiveProfiles). - أي تجاوزات للخصائص (سمة
propertiesأو@TestPropertySource). - أي تصريحات
@MockBeanأو@SpyBean(كل مجموعة فريدة تنشئ سياقًا منفصلًا).
@MockBean في الحد الأدنى اللازم — فكل مجموعة جديدة من @MockBean تفرض إنشاء سياق جديد.
استخدام ملف خصائص تطبيق اختبار مخصص
يجب ألا تُستخدم قواعد بيانات الإنتاج ومفاتيح API في الاختبارات. ضع ملف src/test/resources/application.properties (أو application-test.yml) بجوار مصادر الاختبار. يلتقطه Spring Boot تلقائيًا ويمنحه الأولوية على الملف الرئيسي عند تشغيل الاختبارات.
إن احتاجت مجرد حفنة من الاختبارات خصائص خاصة، فاستخدم سمة properties المضمَّنة بدلًا من ملف — فهي تُبقي التجاوز مرئيًا بجوار التعليق مباشرةً:
@DirtiesContext ما لم تحتجه فعلًا. يُخبر هذا التعليق Spring بتدمير السياق وإعادة بنائه بعد الاختبار الموسوم (فئة أو طريقة). يضمن العزل لكنه يُلغي التخزين المؤقت للسياق كليًا — وهو أحد أكثر أسباب بطء مجموعات الاختبار شيوعًا. افضّل التراجع عن قاعدة البيانات (عبر @Transactional على طريقة الاختبار) أو إدارة دورة حياة Testcontainers لتحقيق العزل.
متى تستخدم @SpringBootTest مقابل اختبارات الشرائح
يوفّر Spring Boot عدة شرائح اختبار مركّزة — @WebMvcTest، و@DataJpaTest، و@JsonTest، وغيرها — تُحمِّل فقط الجزء من السياق المناسب لطبقة واحدة. يكون @SpringBootTest هو الخيار الصحيح حين:
- تحتاج إلى اختبار تكامل طبقات متعددة (مثلًا: المتحكم → الخدمة → المستودع).
- تختبر سلوك التهيئة التلقائية — للتحقق من أن
application.propertiesيُشغِّل حبة معقدة بشكل صحيح. - تكتب اختبارات دخان تؤكد تحميل سياق التطبيق بلا أخطاء (
contextLoads()).
بالنسبة للاختبارات التي تستهدف طبقة واحدة بمعزل عن غيرها، افضّل تعليق الشريحة المناسب — فهو يبدأ بشكل أسرع ويُحدد الأعطال في الطبقة الصحيحة. تستخدم مجموعة الاختبارات المُنظَّمة جيدًا كليهما: اختبارات شرائح لغالبية التغطية وعددًا صغيرًا من اختبارات التكامل بـ@SpringBootTest للتحقق من التوصيل الشامل من البداية إلى النهاية.
الخلاصة
يُشغِّل @SpringBootTest ApplicationContext الكامل لاختباراتك مع تحكم دقيق في بيئة الويب عبر سمة webEnvironment. يعني التخزين المؤقت للسياق أن تكلفة التشغيل المرتفعة تُدفع مرة واحدة لكل تهيئة فريدة — لذا فإن هيكلة اختباراتك لمشاركة التهيئة هي أقوى رافعة للحفاظ على سرعة مجموعة اختبارات التكامل. استخدم ملف application.properties مخصصًا في src/test/resources لاستبدال بنية الإنتاج الأساسية (قواعد البيانات، والطوابير، وواجهات API الخارجية) ببدائل خفيفة للاختبار، وارجع إلى @SpringBootTest عندما تحتاج إلى التحقق من التوصيل عبر الطبقات لا من سلوك مكونات معزولة.