طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 245 · ضمان جودة تطبيقات الويب
الدرس 7 · الوحدة 4: خطط الاختبار البرمجية

إطار unittest: TestCase

من المُشغّل اليدويّ إلى إطارٍ يجمع الاختبارات ويشغّلها ويلخّصها. ⏱ نحو 45 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تكتب اختبارات بإطار unittest.
  • تستخدم دوال التأكيد الجاهزة.
  • تقرأ تقرير الفشل الذي يعرض الفعلي والمتوقَّع.
  • تتجنّب أخطاء التسمية والوراثة.

١من التأكيدات المبعثرة إلى إطار

كتبنا مُشغّلاتٍ يدوية تعدّ النجاح والفشل. العالَم الحقيقيّ يستخدم أطر اختبار جاهزة تفعل ذلك وأكثر. في بايثون، الإطار القياسيّ المدمج هو unittest (مستوحى من JUnit في جافا). تكتب أصنافًا ترث TestCase، ودوالًا تبدأ بـ test_، فيجدها الإطار ويشغّلها ويلخّص النتائج.

main.py
import unittest def total(price, qty):    return price * qty class TestTotal(unittest.TestCase):    def test_basic(self):        self.assertEqual(total(50, 3), 150)    def test_zero_qty(self):        self.assertEqual(total(50, 0), 0)    def test_one(self):        self.assertEqual(total(99, 1), 99) import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)
Shell×
>>> %Run main.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
NO TESTS RAN
>>>

لاحظ الخلاصة: Ran 3 tests ... OK. الإطار جمع الدوال وشغّلها وأبلغ. لا مُشغّل يدويّ بعد اليوم.

٢دوال التأكيد الجاهزة

بدل assert الخام، يقدّم TestCase دوالًا أوضح رسالةً عند الفشل:

الدالةتتحقّقمثال
assertEqual(a, b)a == bassertEqual(total(2,3), 6)
assertTrue(x)x صحيحassertTrue(is_valid(5))
assertFalse(x)x خطأassertFalse(is_empty(cart))
assertIn(a, b)a داخل bassertIn("sa", email)
assertRaises(E)يُرفَع الخطأ Ewith assertRaises(ValueError):
assertAlmostEqual(a,b)تقارب الأعداد العشريةassertAlmostEqual(0.1+0.2, 0.3)

ميزتها: عند الفشل تطبع الفعلي والمتوقَّع معًا تلقائيًّا، فتعرف الخطأ دون رسالةٍ تكتبها بنفسك.

٣قراءة تقرير الفشل

انظر اختبارًا يفشل عمدًا، واقرأ كيف يصف الإطار الخطأ بدقّة:

main.py
import unittest def to_cents(amount):    return amount * 100          # ✗ عيب مقصود: يجب int(round(...)) class TestMoney(unittest.TestCase):    def test_cents(self):        self.assertEqual(to_cents(1.1), 110)   # 1.1*100 = 110.00000000000001 import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)
Shell×
>>> %Run main.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
NO TESTS RAN
>>>

التقرير يقول FAILED ويعرض 110.00000000000001 != 110: كشف خطأ الفاصلة العائمة الذي يخدع العين. هذا ما يجعل الإطار أقوى من الفحص اليدويّ.

توقّع وتحقّق

الإطار القياسيّ للاختبار في بايثون:

دوال الاختبار يجب أن تبدأ بـ:

للتحقّق من تساوي قيمتين:

لمقارنة أعداد عشرية:

الخلاصة

تحدٍّ برمجي: اكتب TestCase

متوسط

اكتب صنف TestCase باسم TestAbs فيه اختباران لدالة my_abs: للمدخل 5 و-5 (المتوقَّع 5)، ثم شغّله.

المطلوب:
  • ورِث unittest.TestCase واكتب دالتي test_.
  • استخدم assertEqual، ثم شغّل بالمُشغّل المعطى.

ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:

main.py
import unittestdef my_abs(n):    return n if n >= 0 else -n# اكتب TestAbs باختبارين ثم شغّلهimport sys
المطلوب×
----------------------------------------------------------------------
Ran 0 tests in 0.000s
NO TESTS RAN
💡 تلميح 1
class TestAbs(unittest.TestCase): ثم دوال test_.
💡 تلميح 2
self.assertEqual(my_abs(-5), 5).

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

main.py
import unittestdef my_abs(n):    return n if n >= 0 else -nclass TestAbs(unittest.TestCase):    def test_pos(self):        self.assertEqual(my_abs(5), 5)    def test_neg(self):        self.assertEqual(my_abs(-5), 5)import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس