التطوير المُوجَّه بالاختبارات (Test-driven Development)

الكاتب: قالب اقرأتاريخ النشر: آخر تحديث: وقت القراءة:
للقراءة
عدد الكلمات:
كلمة
عدد التعليقات: 0 تعليق
نبذة عن المقال: تعلم التطوير الموجه بالاختبارات (TDD) خطوة بخطوة. اكتشف دورة الأحمر-الأخضر-إعادة الهيكلة وكيفية كتابة كود أفضل وأكثر موثوقية باستخدام Vitest.

أهلاً بك في درس جديد! 

بعد أن تعلمت أساسيات كتابة الاختبارات في الدرس السابق وتعرفت على كيفية استخدام Vitest، حان الوقت لننتقل إلى مستوى أعلى ونتعلم منهجية قوية ستغير طريقة تفكيرك في كتابة الشيفرة البرمجية: التطوير المُوجَّه بالاختبارات أو ما يُعرف بـ Test-driven Development واختصاراً TDD.

قد يبدو الأمر غريباً في البداية: كتابة الاختبار قبل كتابة الكود الفعلي؟ لكن مع نهاية هذا الدرس، ستكتشف كيف أن هذه المنهجية لا تضمن جودة الكود فحسب، بل تقودك أيضاً إلى تصميم أفضل وأكثر قابلية للصيانة.

التطوير المُوجَّه بالاختبارات (Test-driven Development)

ما هو التطوير المُوجَّه بالاختبارات (TDD)؟

ببساطة، Test-driven Development هي عملية تطوير برمجيات تعتمد على تكرار دورة قصيرة جداً: أولاً، يكتب المطور اختباراً آلياً يفشل لأنه لم يتم تنفيذ الميزة بعد. ثانياً، يكتب المطور أبسط كود ممكن يجعل هذا الاختبار ينجح. ثالثاً وأخيراً، يقوم المطور بإعادة هيكلة الكود الجديد لتحسينه مع الحفاظ على نجاح الاختبار.

لنتخيل أنك تبني منزلاً من قطع الليغو (LEGO). الطريقة التقليدية هي أن تبدأ في تجميع القطع معاً على أمل أن تصل للشكل النهائي المطلوب. أما منهجية TDD فهي أشبه بوجود كتيب التعليمات. أنت تنظر إلى الخطوة التالية في الكتيب (تكتب اختباراً)، تلاحظ أن القطعة المطلوبة غير موجودة في مكانها (الاختبار يفشل)، ثم تضيف تلك القطعة بالضبط كما هو مطلوب (تكتب الكود)، وأخيراً تتأكد من أن البناء ما زال متيناً ومرتباً (تعيد هيكلة الكود).

دورة حياة TDD: الأحمر-الأخضر-إعادة الهيكلة

تتمحور منهجية TDD حول دورة بسيطة لكنها فعالة جداً تُعرف بـ Red-Green-Refactor.

  1. الأحمر (Red): اكتب اختباراً صغيراً يفشل. هذا هو الجزء الذي يحدد ما يجب على الكود أن يفعله.
  2. الأخضر (Green): اكتب أبسط كود ممكن يجعل الاختبار ينجح. في هذه المرحلة، لا تهتم بجودة الكود أو أناقته، فقط اجعل الشريط أخضر!
  3. إعادة الهيكلة (Refactor): قم بتحسين الكود الذي كتبته. اجعله أنظف، أسهل في القراءة، وأكثر كفاءة، مع التأكد من أن جميع الاختبارات لا تزال ناجحة.

دعنا نطبق هذه الدورة على مثال عملي. لنفترض أننا نريد إنشاء دالة calculateTip لحساب قيمة الإكرامية (البقشيش) بناءً على إجمالي الفاتورة ونسبة مئوية.

1. المرحلة الحمراء (Red): كتابة اختبار فاشل

أولاً، سنبدأ بكتابة اختبار يصف أول متطلب لدينا: يجب أن تحسب الدالة الإكرامية بشكل صحيح. سننشئ ملف tip.test.js.

JavaScript
// tip.test.js
import { describe, it, expect } from 'vitest';
import { calculateTip } from './tip';

describe('calculateTip', () => {
  it('should calculate the tip correctly for a standard percentage', () => {
    // Arrange: Define inputs
    const totalBill = 100;
    const tipPercentage = 15; // 15%

    // Act: Call the function we are testing
    const result = calculateTip(totalBill, tipPercentage);

    // Assert: Check if the result is what we expect
    expect(result).toBe(15);
  });
});

بالطبع، إذا قمنا بتشغيل هذا الاختبار الآن، سيفشل فشلاً ذريعاً. لماذا؟ لأن الدالة calculateTip غير موجودة أصلاً! سيُظهر لك Vitest خطأً مثل ReferenceError: calculateTip is not defined وهذا هو اللون الأحمر الذي نريده. لقد حددنا هدفنا.

نصيحة

فشل الاختبار لسبب وجيه (مثل عدم وجود الدالة) هو خطوة ممتازة في TDD. هذا يؤكد أن اختبارك يعمل بشكل صحيح وأنه لا ينجح بالصدفة.

2. المرحلة الخضراء (Green): كتابة الكود لاجتياز الاختبار

الآن، هدفنا هو جعل هذا الاختبار ينجح بأسرع وأبسط طريقة ممكنة. لن ننشغل بكتابة كود مثالي. سننشئ ملف tip.js ونكتب فيه الكود التالي:

JavaScript
// tip.js
export function calculateTip(total, percentage) {
  return (total * percentage) / 100;
}

هذا هو أبسط كود منطقي يمكن أن يجعل اختبارنا ينجح. الآن، لنعد تشغيل الاختبارات. ماذا ستكون النتيجة؟

أخضر! لقد نجح الاختبار. لقد انتقلنا من المرحلة الحمراء إلى المرحلة الخضراء. قد يبدو الكود بسيطاً، وهذا هو المطلوب تماماً في هذه المرحلة.

3. مرحلة إعادة الهيكلة (Refactor): تحسين الكود

بعد أن أصبح لدينا اختبار ناجح يعمل كشبكة أمان، يمكننا الآن النظر إلى الكود الذي كتبناه ونسأل أنفسنا: "هل يمكن تحسينه؟".

في حالتنا هذه، الكود بسيط جداً وواضح. لا يوجد الكثير مما يمكن إعادة هيكلته. ربما يمكننا إضافة بعض التعليقات أو تحسين أسماء المتغيرات إذا لم تكن واضحة. بما أن الكود الحالي جيد، يمكننا الانتقال مباشرة إلى إضافة متطلب جديد.

ملاحظة هامة

إعادة الهيكلة لا تعني إضافة وظائف جديدة. إنها تعني تحسين الكود الحالي دون تغيير سلوكه الخارجي. يجب أن تظل جميع الاختبارات ناجحة بعد هذه المرحلة.

تطبيق الدورة على متطلب جديد

جميل جداً! الآن لنفترض أن متطلبات المشروع تغيرت. نريد الآن أن تقوم الدالة بتقريب الناتج إلى أقرب منزلتين عشريتين، لأننا نتعامل مع أموال. كيف سنتعامل مع هذا باستخدام TDD؟ بالعودة إلى المرحلة الحمراء!

1. المرحلة الحمراء (Red):

لنضف اختباراً جديداً يصف السلوك المطلوب. سنختبر حالة يكون فيها الناتج يحتوي على أكثر من منزلتين عشريتين.

JavaScript
// tip.test.js
// ... (previous describe block)

describe('calculateTip', () => {
  // ... (previous 'it' block)

  it('should round the tip to two decimal places', () => {
    const totalBill = 10.75;
    const tipPercentage = 12.5;

    // The actual result is 1.34375
    const result = calculateTip(totalBill, tipPercentage);

    // We expect it to be rounded to 1.34
    expect(result).toBe(1.34);
  });
});

عند تشغيل الاختبارات، سينجح الاختبار الأول، لكن هذا الاختبار الجديد سيفشل. لقد عدنا إلى اللون الأحمر. سيخبرنا Vitest أنه توقع 1.34 ولكنه حصل على 1.34375.

2. المرحلة الخضراء (Green):

الآن، لنعدل الكود لجعل الاختبار الجديد ينجح. مرة أخرى، نريد أبسط حل ممكن.

JavaScript
// tip.js
export function calculateTip(total, percentage) {
  const tip = (total * percentage) / 100;
  // Let's round the result
  return parseFloat(tip.toFixed(2));
}

تحذير

استخدام toFixed يعيد سلسلة نصية (string)، لذلك نستخدم parseFloat لتحويلها مرة أخرى إلى رقم. هذا مثال جيد على كتابة كود بسيط قد لا يكون هو الحل الأمثل، لكنه يجعل الاختبار ينجح.

عند تشغيل الاختبارات مرة أخرى، ستنجح جميعها. لقد عدنا إلى اللون الأخضر.

3. مرحلة إعادة الهيكلة (Refactor):

الآن ننظر إلى الكود. هل هناك طريقة أفضل للتقريب؟ قد يكون استخدام Math.round أكثر كفاءة وأماناً من التحويل بين الرقم والسلسلة النصية.

JavaScript
// tip.js
export function calculateTip(total, percentage) {
  const tip = (total * percentage) / 100;
  // A more robust way to round to 2 decimal places
  return Math.round(tip * 100) / 100;
}

بعد هذا التعديل، ماذا نفعل؟ بالطبع، نعيد تشغيل جميع الاختبارات! هذا هو الجزء الأهم. إذا بقيت جميعها خضراء، فهذا يعني أن إعادة الهيكلة كانت ناجحة. لقد قمنا بتحسين الكود الداخلي دون كسر أي وظيفة.

فوائد اتباع نهج TDD

قد تشعر أن هذه العملية أبطأ قليلاً من كتابة الكود مباشرة، ولكن الفوائد على المدى الطويل هائلة:

  • ثقة أكبر: كل جزء من الكود مدعوم باختبار. يمكنك إجراء تغييرات وإعادة هيكلة بثقة، مع العلم أن اختباراتك ستنبهك إذا قمت بكسر شيء ما.
  • تصميم أفضل: يجبرك TDD على التفكير في كيفية استخدام الكود الخاص بك قبل كتابته. هذا يؤدي غالبًا إلى واجهات برمجية (APIs) أبسط وأكثر تركيزًا.
  • توثيق حي: الاختبارات نفسها تعمل كتوثيق حي ودقيق لكيفية عمل الكود الخاص بك وما هي الحالات التي يغطيها.
  • تقليل الأخطاء: اكتشاف الأخطاء مبكراً في دورة التطوير أرخص وأسهل بكثير من اكتشافها لاحقاً في مرحلة الإنتاج.
  • تغطية اختبار 100% (تقريبًا): بما أنك لا تكتب أي كود إنتاجي إلا إذا كان هناك اختبار فاشل له، فإنك تضمن بطبيعة الحال أن كل الكود الخاص بك مغطى بالاختبارات.

الخلاصة

التطوير المُوجَّه بالاختبارات (TDD) هو أكثر من مجرد كتابة اختبارات؛ إنه منهجية لتصميم وتطوير البرمجيات. من خلال اتباع دورة Red-Green-Refactor، فإنك تبني برامجك خطوة بخطوة، مع التأكد من جودة كل خطوة قبل الانتقال إلى التالية.

قد يتطلب الأمر بعض التدريب لتعتاد على التفكير "الاختبار أولاً"، ولكن بمجرد أن تتقنه، ستجد أنه أداة قوية في جعبتك كمطور. في التمرين التالي، ستحصل على فرصة لتطبيق ما تعلمته اليوم بنفسك!

قد تُعجبك هذه المشاركات

إرسال تعليق

ليست هناك تعليقات

7627059358572141466

العلامات المرجعية

قائمة العلامات المرجعية فارغة ... قم بإضافة مقالاتك الآن

    البحث