تمرين: اختبار دالة الـ Factorial

الكاتب: قالب اقرأتاريخ النشر: آخر تحديث: وقت القراءة:
للقراءة
عدد الكلمات:
كلمة
عدد التعليقات: 0 تعليق
نبذة عن المقال: تمرين عملي على تطبيق منهجية التطوير الموجه بالاختبار (TDD) لكتابة واختبار دالة Factorial باستخدام Vitest، مع تغطية الحالات الأساسية والاستثنائية.

في الدرس السابق، تعرفنا على مفهوم التطوير الموجه بالاختبار (Test-driven Development - TDD). الآن حان الوقت لتطبيق ما تعلمناه بشكل عملي. في هذا التمرين، سنقوم ببناء دالة لحساب المضروب (Factorial) لعدد ما، ولكن هذه المرة سنتبع دورة TDD خطوة بخطوة: الأحمر، الأخضر، إعادة الهيكلة (Red, Green, Refactor).

المضروب لعدد صحيح غير سالب n، والذي يُرمز له بـ !n، هو حاصل ضرب جميع الأعداد الصحيحة الموجبة الأقل من أو تساوي n. على سبيل المثال، !5 = 5 × 4 × 3 × 2 × 1 = 120. حالة خاصة ومهمة هي أن !0 = 1.

هيا بنا نبدأ رحلتنا في بناء هذه الدالة واختبارها باستخدام TDD.

تمرين: اختبار دالة الـ Factorial

الخطوة الأولى: كتابة اختبار فاشل (Red)

كما تعلمنا، تبدأ دورة TDD دائمًا بكتابة اختبار يفشل. هذا يضمن أننا نكتب الكود لسبب وجيه وهو تلبية متطلبات الاختبار.

لنبدأ بأبسط حالة ممكنة. في الرياضيات، مضروب الصفر (!0) هو 1. هذه نقطة انطلاق ممتازة.

أولاً، قم بإنشاء ملف اختبار جديد باسم factorial.test.js.

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

describe('factorial', () => {
  it('should return 1 for factorial of 0', () => {
    // Arrange: No arrangement needed for this simple case

    // Act: Call the function with the input
    const result = factorial(0);

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

عند محاولة تشغيل هذا الاختبار، سيفشل على الفور. لماذا؟ لسببين:

  1. ملف factorial.js الذي يحتوي على الدالة factorial غير موجود أصلاً.
  2. بالتالي، الدالة factorial نفسها غير موجودة.

هذا هو اللون الأحمر الذي نبحث عنه. لقد أثبتنا أن الاختبار يمكن أن يفشل.

الخطوة الثانية: كتابة أبسط كود لتجاوز الاختبار (Green)

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

قم بإنشاء ملف factorial.js واكتب فيه الكود التالي:

JavaScript
// factorial.js
export const factorial = (n) => {
  return 1;
};

هذا الكود بسيط جداً، بل قد يبدو خاطئاً! لكن هل يفي بمتطلبات اختبارنا الحالي؟ نعم. عندما يتم استدعاء factorial(0)، ستُرجع الدالة القيمة 1، وهذا بالضبط ما يتوقعه الاختبار.

قم بتشغيل الاختبارات مرة أخرى. ستجد أن الاختبار ينجح الآن. لقد انتقلنا إلى المرحلة الخضراء (Green).

ملاحظة

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

الخطوة الثالثة: إعادة الهيكلة (Refactor)

في هذه المرحلة، ننظر إلى الكود الذي كتبناه ونتساءل: هل يمكن تحسينه؟ هل هو واضح؟ هل هناك تكرار؟

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

التكرار: إضافة حالة اختبار جديدة (Red Again)

الآن بعد أن غطينا حالة الصفر، لنضف حالة أخرى. ماذا عن مضروب العدد 1؟ يجب أن يكون 1 أيضاً. لنضف اختباراً جديداً لذلك.

JavaScript
// factorial.test.js
// ... (الكود السابق)
describe('factorial', () => {
  it('should return 1 for factorial of 0', () => {
    expect(factorial(0)).toBe(1);
  });

  it('should return 1 for factorial of 1', () => {
    expect(factorial(1)).toBe(1);
  });
});

عند تشغيل الاختبارات، ستلاحظ أن هذا الاختبار الجديد ينجح أيضاً! هذا لأن تطبيقنا الحالي يُرجع 1 دائماً. هذا جيد، لكنه لا يدفعنا لكتابة منطق جديد. لذا، نحتاج إلى حالة اختبار تجبرنا على تغيير الكود.

لنجرب حالة أكثر تعقيداً: مضروب العدد 5، والذي يجب أن يكون 120.

JavaScript
// factorial.test.js
// ... (الكود السابق)
describe('factorial', () => {
  // ... (الاختبارات السابقة)

  it('should return 120 for factorial of 5', () => {
    expect(factorial(5)).toBe(120);
  });
});

الآن، عند تشغيل الاختبارات، سيفشل هذا الاختبار الأخير بالتأكيد. لقد عدنا إلى المرحلة الحمراء (Red).

العودة إلى الأخضر: تطبيق المنطق الصحيح

حان الوقت لتحديث دالة factorial لتلبية جميع متطلبات الاختبارات الحالية. نحتاج الآن إلى تطبيق المنطق الفعلي لحساب المضروب.

JavaScript
// factorial.js
export const factorial = (n) => {
  if (n === 0) {
    return 1;
  }

  let result = 1;
  for (let i = n; i > 0; i--) {
    result *= i;
  }
  return result;
};

هذا التنفيذ يتعامل الآن مع حالة الصفر بشكل صريح، ثم يستخدم حلقة تكرارية لحساب المضروب للأرقام الأكبر من الصفر. قم بتشغيل الاختبارات مرة أخرى. الآن يجب أن تنجح جميع الاختبارات الثلاثة. لقد عدنا إلى المرحلة الخضراء (Green).

التعامل مع الحالات الاستثنائية (Edge Cases)

دورة TDD لا تقتصر فقط على الحالات الصحيحة (happy paths). يجب أن نفكر أيضاً في المدخلات غير الصالحة. ماذا يجب أن يحدث إذا حاول شخص ما حساب مضروب عدد سالب؟ رياضياً، المضروب غير معرف للأعداد السالبة. لذلك، يجب أن تُطلق دالتنا خطأ (throw an error) في هذه الحالة.

لنكتب اختباراً جديداً لهذه الحالة (Red):

JavaScript
// factorial.test.js
// ... (الكود السابق)
describe('factorial', () => {
  // ... (الاختبارات السابقة)

  it('should throw an error for negative numbers', () => {
    // We expect the function call inside the callback to throw an error.
    expect(() => factorial(-1)).toThrow('Invalid input: Factorial is not defined for negative numbers.');
  });
});

نصيحة احترافية

لاحظ طريقة كتابة الاختبار للأخطاء. نحن نمرر دالة مجهولة (() => factorial(-1)) إلى expect. هذا يسمح لمكتبة الاختبار بالتقاط الخطأ الذي تم إطلاقه والتحقق منه. إذا استدعيت factorial(-1) مباشرة داخل expect، سيتوقف الاختبار بالكامل بسبب الخطأ ولن يتمكن من تأكيد أي شيء.

عند تشغيل الاختبارات، سيفشل هذا الاختبار الجديد لأن دالتنا الحالية لا تُطلق أي خطأ.

الآن، لنعدل الكود لجعله ينجح (Green):

JavaScript
// factorial.js
export const factorial = (n) => {
  if (n < 0) {
    throw new Error('Invalid input: Factorial is not defined for negative numbers.');
  }

  if (n === 0) {
    return 1;
  }

  let result = 1;
  for (let i = n; i > 0; i--) {
    result *= i;
  }
  return result;
};

قمنا بإضافة شرط في بداية الدالة للتحقق مما إذا كان الإدخال سالباً. إذا كان كذلك، فإننا نطلق خطأ مع رسالة واضحة.

شغل الاختبارات للمرة الأخيرة. الآن، يجب أن تنجح جميع الاختبارات.

الخلاصة

لقد قمنا ببناء دالة factorial قوية وموثوقة باتباع منهجية TDD. لاحظ كيف أرشدتنا الاختبارات خلال عملية التطوير:

  1. بدأنا بأبسط حالة ممكنة (!0).
  2. كتبنا الكود الأدنى لجعل الاختبار ينجح.
  3. أضفنا حالة أكثر تعقيداً (!5) أجبرتنا على تنفيذ المنطق الحقيقي.
  4. فكرنا في الحالات الاستثنائية (الأعداد السالبة) وأضفنا اختباراً لها.

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

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

إرسال تعليق

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

7627059358572141466

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

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

    البحث