أساسيات unittest
unittest Basics
أساسيات unittest — unittest Basics
قبل أن يُكتب أي كود في مشروع جاد، يُكتب اختبار. هذه الفلسفة تحمي كل تغيير تجريه لاحقاً. Python تأتي بحزمة unittest في المكتبة المعيارية — لا تحتاج تثبيت أي شيء، فقط import unittest وتبدأ.
مفهوم الاختبار الآلي
الاختبار الآلي (Automated Test) هو كود يتحقق من أن كوداً آخر يتصرف بالشكل المتوقع. عوضاً عن تشغيل البرنامج يدوياً وتفحّص المخرجات بعينيك، تكتب مرة واحدة توقعاتك وتشغّل الأداة كلما احتجت.
الفائدة الحقيقية تظهر عند التعديل: عندما تُعيد كتابة دالة أو تُضيف ميزة، تشغّل الاختبارات وتعرف على الفور إذا كسرت شيئاً كان يعمل. بدون اختبارات، كل تغيير يحتمل كسر أجزاء لم تُلاحظها.
بنية اختبار unittest
نمط unittest يدور حول ثلاثة عناصر:
- TestCase — كلاس ترث منه اختباراتك (
unittest.TestCase) - دوال الاختبار — كل دالة تبدأ بـ
test_تُعامَل كاختبار مستقل - Assertions — دوال تتحقق من التوقعات، مثل
assertEqualوassertTrue
import unittest
class TestMyFunction(unittest.TestCase):
def test_something(self):
result = my_function()
self.assertEqual(result, expected_value)
أول اختبار حقيقي
لاحظ الناتج: كل دالة اختبار تُبلَّغ منفصلاً. OK تعني نجاح. لو فشل اختبار، يُظهر FAIL مع سبب التفصيلي.
setUp و tearDown
أحياناً تحتاج إعداداً مشتركاً قبل كل اختبار — قاعدة بيانات مؤقتة، كائن مهيّأ، ملف اختبار. setUp تُشغَّل قبل كل اختبار، وtearDown تُشغَّل بعد كل اختبار حتى لو فشل.
هذا يضمن أن كل اختبار يبدأ بحالة نظيفة ولا يتأثر بنتائج الاختبارات السابقة.
Assertions الشائعة
unittest.TestCase توفّر مجموعة واسعة من دوال التحقق. إليك الأكثر استخداماً:
| الدالة | متى تستخدمها |
|---|---|
assertEqual(a, b) | عندما تتوقع أن a == b |
assertNotEqual(a, b) | عندما تتوقع أن a != b |
assertTrue(x) | عندما تتوقع أن x صحيحة (truthy) |
assertFalse(x) | عندما تتوقع أن x خاطئة (falsy) |
assertIsNone(x) | عندما تتوقع x is None |
assertIsNotNone(x) | عندما تتوقع x is not None |
assertIn(a, b) | عندما تتوقع أن a in b |
assertRaises(exc) | عندما تتوقع رفع استثناء |
assertAlmostEqual(a, b) | للأرقام العشرية (تتجاهل الفارق الصغير) |
استخدام assertRaises بشكل صحيح
assertRaises له أسلوبان: كـcontext manager (الأوضح)، أو كدالة مباشرة.
# الأسلوب الأوضح: context manager
def test_invalid_input(self):
with self.assertRaises(ValueError) as ctx:
divide(5, 0)
# يمكن التحقق من رسالة الخطأ
self.assertIn("صفر", str(ctx.exception))
# الأسلوب البديل: أقل وضوحاً
def test_invalid_input_alt(self):
self.assertRaises(ValueError, divide, 5, 0)
الأسلوب الأول أفضل لأنه يُتيح التحقق من رسالة الاستثناء أيضاً.
تشغيل الاختبارات في مشروع حقيقي
في مشروع حقيقي، الاختبارات في ملفات منفصلة:
my_project/
├── calculator.py # الكود الأصلي
└── test_calculator.py # الاختبارات
ثم تشغّل من Terminal:
# شغّل اختبارات ملف واحد
python -m unittest test_calculator
# شغّل كل الاختبارات في المجلد
python -m unittest discover
# مع تفاصيل — verbose
python -m unittest -v test_calculator
الخيار -m unittest يجعل Python تشغّل الوحدة كأداة سطر أوامر.
تنظيم الاختبارات — أفضل الممارسات
- اسم واضح للاختبار —
test_add_two_positive_numbersأفضل منtest1 - اختبار واحد يختبر فكرة واحدة — لا تختبر خمسة سلوكيات في دالة واحدة
- اختبر الحالات الحدية — صفر، قيمة فارغة، قيمة سالبة، قيمة أقصى
- لا تعتمد ترتيب التشغيل — كل اختبار مستقل عن الآخر
- رسالة خطأ واضحة —
assertEqual(result, 5, "يجب أن يكون الجمع 5")يساعد عند الفشل