التطوير بقيادة الاختبارات (TDD)
التطوير بقيادة الاختبارات (TDD)
التطوير بقيادة الاختبارات منهجية عمل لا مجرد تقنية. تكتب اختبارًا فاشلًا قبل أي كود إنتاجي، ثم تجعله ينجح بأقل تغيير ممكن، ثم تنظّف الكود. هذا التسلسل — أحمر ← أخضر ← إعادة هيكلة — هو نبضة قلب TDD. حين تلتزم بها تُنتج كودًا صحيحًا بالبناء، ضيّق النطاق، وسهل التغيير لأن كل سلوك تُغطيه اختبارات منذ اللحظة الأولى.
دورة أحمر-أخضر-إعادة الهيكلة بالتفصيل
- أحمر — اكتب اختبارًا للسلوك الصغير التالي. شغّله. يجب أن يفشل، مما يُثبت أن الاختبار يُمارس شيئًا غير موجود بعد. اختبار ينجح فورًا ليس خطوة حمراء؛ بل إشارة إلى خطأ في الاختبار أو أن السلوك موجود مسبقًا.
- أخضر — اكتب الحد الأدنى من الكود الإنتاجي لتمرير الاختبار. قاوم إغراء كتابة أكثر مما يتطلبه الاختبار. إعادة قيمة ثابتة مقبولة هنا إن جعلت الاختبار يمرّ؛ الاختبار التالي سيجبرك على التعميم.
- إعادة الهيكلة — مع كون جميع الاختبارات خضراء، حسّن التصميم: أزل التكرار، وضّح الأسماء، استخرج الدوال، طبّق الأنماط. مجموعة الاختبارات هي شبكة أمانك. إن أصبح اختبار أحمر أثناء إعادة الهيكلة فقد أدخلت تراجعًا — ارجع وحاول من جديد.
مثال عملي متكامل: حسابات المال
سنبني بمنهجية TDD فئة Money بسيطة تجمع مبالغ بنفس العملة. نبدأ بلا شيء سوى ملف الاختبار.
التكرار 1 — أحمر: كائن Money يحفظ مبلغه
تشغيل الاختبار يفشل بخطأ تجميع: Money غير موجودة. هذه هي الخطوة الحمراء.
التكرار 1 — أخضر: الحد الأدنى من الكود
الاختبار الآن أخضر. قاومنا إضافة add() أو أي شيء آخر لا يتطلبه الاختبار.
التكرار 2 — أحمر: جمع أموال بنفس العملة
أحمر: add() غير موجودة.
التكرار 2 — أخضر
أخضر. أما مرحلة إعادة الهيكلة: هل اسم amount واضح؟ كمُستخرِج في record فنعم. لا تكرار. نكمل.
التكرار 3 — أحمر: عملات مختلفة يجب أن ترمي استثناءً
التكرار 3 — أخضر
التكرار 3 — إعادة الهيكلة
الاختبارات الثلاثة ناجحة. يمكننا استخراج الحارس في دالة خاصة لتوضيح القصد:
جميع الاختبارات لا تزال خضراء. إعادة الهيكلة كانت آمنة.
تقنية التثليث
حين تكون الخطوة الخضراء "غشًا" (مثل return 15;)، تُثلّث بكتابة اختبار ثانٍ يُجبرك على تنفيذ حقيقي. مثلًا، اختبار لـ add(3, 5) يُعيد 8 وآخر لـ add(7, 2) يُعيد 9 يُجبرك على كتابة return a + b بدلًا من تثبيت 8.
TDD وضغط التصميم
TDD يمارس ضغطًا مستمرًا على التصميم. إن كان كتابة الاختبار مؤلمًا — تحتاج بناء كائنات كثيرة، أو الوصول لتفاصيل داخلية، أو محاكاة دالة ثابتة — فالاختبار يُخبرك أن التصميم خاطئ. رائحات شائعة وإصلاحاتها بقيادة TDD:
- صعب الاستنساخ — الفئة تحمل مسؤوليات كثيرة. قسّمها.
- وجوب محاكاة قاعدة البيانات في اختبار وحدة — منطق المجال مقترن بالمثابرة. أضف واجهة repository وحقنها.
- اختبار دالة خاصة — الدوال الخاصة تفاصيل تنفيذية. اختبر العقد العام؛ إن كان المنطق الخاص معقدًا كفاية ليحتاج اختباره الخاص، استخرجه في فئة متعاونة.
- إعداد الاختبار يمتد 30 سطرًا — الموضوع له تبعيات كثيرة جدًا. طبّق مبدأ المسؤولية الواحدة.
تطبيق TDD على طبقة الخدمات
في العالم الحقيقي، TDD غالبًا يتضمن تبعيات محقونة. المفتاح هو كتابة الاختبار أولًا مع محاكاة، مما يُجبرك على تعريف واجهة المتعاون قبل تنفيذه.
هذا الاختبار يُعرّف الواجهة البرمجية العامة لـ OrderService — الدالة placeOrder، وواجهات المتعاونين (OrderRepository، PaymentGateway)، والتفاعل المتوقع — كل ذلك قبل كتابة سطر واحد من OrderService.
الخلاصة
TDD حلقة تغذية راجعة ضيّقة: أحمر ← أخضر ← إعادة هيكلة، تتكرر عشرات المرات في الساعة. تُبقي الكود في حده الأدنى، وتُجبر على تصميم جيد، وتضمن تغطية كل سلوك من لحظة وجوده. الأمثلة المُقدَّمة هنا — سجل Money وOrderService — تُظهر النمط ذاته على مستوى كائن القيمة وطبقة الخدمات. احمل هذا الإيقاع في كل ميزة جديدة وستكتب جافا أقل أخطاءً وأسهل صيانة.