لماذا TDD ليست مجرد منهجية عصرية أخرى، بل عباءتك الخارقة الجديدة
تخيل: أنت تكتب الكود كفنان - ضربة تلو الأخرى، سطراً تلو الآخر. أنت واثق أن كل شيء عبقري. لكن تمر ساعة، وتشغل التطبيق، فينهار بخطأ كنت ستبحث عنه لنصف يوم. هل يبدو ذلك مألوفاً؟
والآن تخيل سيناريو آخر. أنت أولاً تكتب اختباراً سيفشل بالتأكيد. ثم - الحد الأدنى من الكود لاجتياز الاختبار. وبعدها فقط - تعيد الهيكلة، وتحسن، ولكن بضمير مرتاح. هذا هو التطوير القائم على الاختبارات (TDD)، وفي Kotlin يتحول إلى متعة حقيقية.
في هذه المقالة، لن نلقي محاضرات مملة. سنأخذ مثالاً واقعياً، ونمر بجميع مراحل TDD الثلاث (Red - Green - Refactor) ونوضح كيف تكتب كوداً موثوقاً دون خوف أو تردد. هل أنت مستعد؟ هيا بنا!
TDD: ثلاثة أحرف ستغير كودك
TDD (Test-Driven Development) ليست مجرد تقنية، بل هي فلسفة. الجوهر بسيط: الاختبار أولاً، ثم الكود. يبدو جنونياً؟ للوهلة الأولى - نعم. عملياً - يوفر ساعات من التصحيح.
دورة التطوير القائم على الاختبارات
- Red - نكتب اختباراً لا ينجح (يجب أن يفشل).
- Green - نكتب الحد الأدنى من الكود لاجتياز الاختبار.
- Refactor - نحسن الكود دون تغيير سلوكه.
وهكذا - في حلقة مفرغة، بخطوات صغيرة. كل خطوة هي ثقة بأنك لم تكسر شيئاً. في Kotlin بتركيبه المختصر وأمانه الصفري، يصبح TDD أكثر متعة.
الأدوات: ما تحتاجه لـ TDD في Kotlin
قبل الغوص في الكود، دعنا نجهز الترسانة. لـ اختبار الوحدة في Kotlin ستحتاج إلى:
- JUnit 5 - المعيار الفعلي لاختبارات الوحدة.
- MockK - مكتبة للـ Mock تم إنشاؤها خصيصاً لـ Kotlin (لا مزيد من المشاكل مع null و companion object).
- Gradle أو Maven - لإدارة التبعيات.
أضف في build.gradle.kts:
dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") testImplementation("io.mockk:mockk:1.13.8")}جاهز. الآن يمكنك كتابة كود لا تخجل من عرضه على زملائك.
نكتب أول دورة TDD: آلة حاسبة طموحة
لنبدأ بالكلاسيكيات - آلة حاسبة. لكن ليست عادية، بل مع دعم العمليات النصية. المهمة: دالة calculate تستقبل سلسلة نصية مثل "2 + 3" وتعيد 5.
الخطوة 1: Red - نكتب اختباراً فاشلاً
ننشئ صنف الاختبار:
import org.junit.jupiter.api.Testimport org.junit.jupiter.api.Assertions.assertEquals
class CalculatorTest {
@Test fun `should add two numbers`() { val calculator = StringCalculator() val result = calculator.calculate("2 + 3") assertEquals(5, result) }}نشغله - يفشل الاختبار لأن الصنف StringCalculator غير موجود بعد. إشارة حمراء - كل شيء يسير وفق الخطة.
الخطوة 2: Green - الحد الأدنى من الكود لاجتياز الاختبار
نكتب بالضبط ما يكفي لاجتياز الاختبار. لا تعقيدات معمارية:
class StringCalculator { fun calculate(expression: String): Int { val parts = expression.split(" + ") return parts[0].toInt() + parts[1].toInt() }}الآن الاختبار أخضر. مرحى! لكن الكود لا يزال قبيحاً - فهو مخصص فقط لجمع رقمين.
الخطوة 3: Refactor - نجعل الكود جميلاً
نعيد الهيكلة، لكن دون تغيير السلوك. نضيف دعم عمليات مختلفة، ونستخرج المنطق في دوال منفصلة:
class StringCalculator { fun calculate(expression: String): Int { val (a, op, b) =