اختبار التكامل باستخدام TestRestTemplate
اختبار التكامل باستخدام TestRestTemplate
اختبارات الوحدة واختبارات الشرائح سريعة ومركّزة، غير أنها تترك سؤالًا جوهريًا دون إجابة: هل يعمل المكدّس بأكمله معًا كما ينبغي؟ تسدّ اختبارات التكامل هذه الفجوة. تتيح لك TestRestTemplate في Spring Boot إطلاق طلبات HTTP حقيقية على خادم تطبيق مُشغَّل بالكامل، واجتياز كل طبقة — مرشّحات الأمان، والمتحكمات، والخدمات، وقاعدة البيانات — والتحقق من استجابات HTTP الفعلية التي يتلقاها أي عميل حقيقي.
الفرق بين اختبارات التكامل واختبارات الشرائح
تحمّل اختبارات الشرائح كـ @WebMvcTest و @DataJpaTest شريحةً رفيعة فحسب من سياق Spring. وهي سريعة تحديدًا لأنها تتجاوز معظم أجزاء التطبيق. أما اختبارات التكامل فتعتمد المنهج المعاكس: تُقلع سياق ApplicationContext الكامل، وتُشغّل خادم Tomcat مُضمَّنًا على منفذ حقيقي، وتتيح لك إرسال طلبات HTTP كأي عميل خارجي. المقايضة واضحة — يستغرق الإعداد عدة ثوانٍ، لذا يجب أن تغطّي اختبارات التكامل رحلات المستخدم الكاملة لا كل فرع صغير من منطق التطبيق.
@SpringBootTest مع منفذ حقيقي
لتشغيل خادم HTTP حقيقي في الاختبارات، اضبط webEnvironment على RANDOM_PORT (أو DEFINED_PORT). يربط Spring Boot عندئذٍ منفذًا متاحًا ويُحقن قيمته في فئة الاختبار عبر @LocalServerPort.
@LocalServerPort بأي منفذ اختاره Spring، فتبني دائمًا عناوين URL بالقيمة الصحيحة.
TestRestTemplate مقابل RestTemplate
تُغلّف TestRestTemplate الـ RestTemplate القياسية في Spring وتُضيف إليها سلوكيات ملائمة للاختبار. والأهم أنها لا تُلقي استثناءات عند استجابات 4xx/5xx — بل تعيد ResponseEntity يحمل حالة الخطأ كي تتمكن من التحقق من سيناريوهات الخطأ بشكل نظيف. كما تُعطّل متابعة إعادة التوجيه افتراضيًا، وهو ما تحتاجه تحديدًا عند اختبار منطق إعادة التوجيه.
تحصل على هذا الـ Bean مجانًا من الضبط التلقائي لاختبارات Spring Boot عند استخدام RANDOM_PORT أو DEFINED_PORT — لا حاجة لأي إعداد يدوي.
اختبار العمليات الأربع: الإنشاء والقراءة والتحديث والحذف
تستعرض مجموعة اختبارات التكامل الواقعية كامل واجهة CRUD لمورد ما:
إرسال الترويسات واختبار الأمان
تتطلب التطبيقات الحقيقية ترويسات مصادقة. استخدم TestRestTemplate.withBasicAuth() لمصادقة HTTP Basic، أو أنشئ HttpEntity مخصصًا يحمل الترويسات المطلوبة لمصادقة Bearer Token:
@Sql أو في @BeforeEach يستدعي المستودع مباشرةً) بدلًا من الاعتماد على بيانات الإنتاج. يُبقي هذا اختباراتك محددة النتائج ومستقلة عن أي بيانات في قاعدة البيانات وقت التشغيل.
حالة قاعدة البيانات في اختبارات التكامل
نظرًا لتشغيل سياق التطبيق الكامل، تكون جميع التغييرات على البيانات حقيقية. كل اختبار يُعدّل قاعدة البيانات قد يُلوّث الاختبار التالي ما لم تُعيد ضبط الحالة. ثمة ثلاث استراتيجيات شائعة:
- @Transactional على فئة الاختبار — تتراجع عن التغييرات بعد كل اختبار. تعمل جيدًا في الاختبارات غير المعتمدة على HTTP، لكن ليس لها أثر هنا لأن
TestRestTemplateترسل الطلبات عبر مقبس HTTP حقيقي؛ ويعمل الخادم في نطاق معاملة مختلف. - @Sql للتنظيف — زيّن أسلوب الاختبار أو فئته بـ
@Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = "cleanup.sql"). صريح وموثوق. - قاعدة بيانات اختبار منفصلة بحالة معروفة — هيّئ
application-test.propertiesلاستخدام قاعدة H2 في الذاكرة واضبطspring.jpa.hibernate.ddl-auto=create-drop. يُعاد إنشاء المخطط من الصفر في كل تشغيل.
فعّل هذا الملف الشخصي في فئة الاختبار بإضافة @ActiveProfiles("test") إلى قائمة التعليقات التوضيحية.
التحقق من أجسام الاستجابة
يُفضَّل التحقق من كائنات استجابة مكتوبة بدقة بدلًا من سلاسل JSON الخام. حين تُمرر فئةً إلى getForEntity، يُفكّك Spring جسمَ الاستجابة تلقائيًا باستخدام Jackson. للتعامل مع المجموعات استخدم ParameterizedTypeReference:
اعتبارات الأداء
اختبارات التكامل أبطأ بطبيعتها من اختبارات الوحدة. اتبع هذه الممارسات للحفاظ على سرعة مجموعة الاختبارات:
- إعادة استخدام سياق التطبيق. يُخزّن Spring Boot السياق مؤقتًا بين فئات الاختبار ذات الإعداد المتماثل افتراضيًا. تجنب إضافة
@MockBeanإلى اختبارات التكامل — فكل مجموعة فريدة من الـ Beans المحاكاة تُنشئ سياقًا منفصلًا وتُضاعف تكلفة الإقلاع. - احتفظ باختبارات التكامل في مجموعة مصادر أو حزمة مخصصة. شغّلها بشكل منفصل عن اختبارات الوحدة كي يحصل المطورون على ردود فعل سريعة أثناء التطوير وتغطية كاملة في CI.
- استخدم Testcontainers لقواعد البيانات المشابهة للإنتاج. في الدرس التالي ستتعلم كيف يستبدل Testcontainers قاعدة H2 بحاوية MySQL أو PostgreSQL حقيقية، مما يُلغي مفاجآت التوافق مع H2 دون فقدان العزل.
الخلاصة
تمنحك TestRestTemplate مع @SpringBootTest(webEnvironment = RANDOM_PORT) اختبارات حقيقية من البداية إلى النهاية: سياق Spring كامل، وخادم HTTP حقيقي، والقدرة على اجتياز كل طبقة من تطبيقك بطلبات HTTP فعلية. هي المحاكاة الأكثر أمانةً لسلوك الإنتاج المتاحة دون نشر فعلي. استخدمها لرحلات المستخدم الحرجة، واضبط حالة قاعدة البيانات لكل اختبار، واعتمد على ResponseEntity في التحقق من سيناريوهات الخطأ، وابق المجموعة مُجدِيّة للحفاظ على سرعة البناء.