إطار unittest: TestCase
من المُشغّل اليدويّ إلى إطارٍ يجمع الاختبارات ويشغّلها ويلخّصها. ⏱ نحو 45 دقيقة.
- تكتب اختبارات بإطار
unittest. - تستخدم دوال التأكيد الجاهزة.
- تقرأ تقرير الفشل الذي يعرض الفعلي والمتوقَّع.
- تتجنّب أخطاء التسمية والوراثة.
١من التأكيدات المبعثرة إلى إطار
كتبنا مُشغّلاتٍ يدوية تعدّ النجاح والفشل. العالَم الحقيقيّ يستخدم أطر اختبار جاهزة تفعل ذلك وأكثر. في بايثون، الإطار القياسيّ المدمج هو unittest (مستوحى من JUnit في جافا). تكتب أصنافًا ترث TestCase، ودوالًا تبدأ بـ test_، فيجدها الإطار ويشغّلها ويلخّص النتائج.
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)
لاحظ الخلاصة: Ran 3 tests ... OK. الإطار جمع الدوال وشغّلها وأبلغ. لا مُشغّل يدويّ بعد اليوم.
٢دوال التأكيد الجاهزة
بدل assert الخام، يقدّم TestCase دوالًا أوضح رسالةً عند الفشل:
| الدالة | تتحقّق | مثال |
|---|---|---|
assertEqual(a, b) | a == b | assertEqual(total(2,3), 6) |
assertTrue(x) | x صحيح | assertTrue(is_valid(5)) |
assertFalse(x) | x خطأ | assertFalse(is_empty(cart)) |
assertIn(a, b) | a داخل b | assertIn("sa", email) |
assertRaises(E) | يُرفَع الخطأ E | with assertRaises(ValueError): |
assertAlmostEqual(a,b) | تقارب الأعداد العشرية | assertAlmostEqual(0.1+0.2, 0.3) |
ميزتها: عند الفشل تطبع الفعلي والمتوقَّع معًا تلقائيًّا، فتعرف الخطأ دون رسالةٍ تكتبها بنفسك.
٣قراءة تقرير الفشل
انظر اختبارًا يفشل عمدًا، واقرأ كيف يصف الإطار الخطأ بدقّة:
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)
التقرير يقول FAILED ويعرض 110.00000000000001 != 110: كشف خطأ الفاصلة العائمة الذي يخدع العين. هذا ما يجعل الإطار أقوى من الفحص اليدويّ.
توقّع وتحقّق
الإطار القياسيّ للاختبار في بايثون:
دوال الاختبار يجب أن تبدأ بـ:
للتحقّق من تساوي قيمتين:
لمقارنة أعداد عشرية:
الخلاصة
unittestإطار بايثون القياسيّ: أصناف ترثTestCaseودوال تبدأ بـtest_.- دوال التأكيد
(assertEqual, assertTrue, assertRaises...)أوضح منassertالخام. - عند الفشل يعرض الإطار الفعلي والمتوقَّع تلقائيًّا.
assertAlmostEqualللأعداد العشرية تفاديًا لخطأ الفاصلة العائمة.
تحدٍّ برمجي: اكتب TestCase
متوسطاكتب صنف TestCase باسم TestAbs فيه اختباران لدالة my_abs: للمدخل 5 و-5 (المتوقَّع 5)، ثم شغّله.
- ورِث
unittest.TestCaseواكتب دالتيtest_. - استخدم
assertEqual، ثم شغّل بالمُشغّل المعطى.
ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:
import unittestdef my_abs(n): return n if n >= 0 else -n# اكتب TestAbs باختبارين ثم شغّلهimport sys
💡 تلميح 1
class TestAbs(unittest.TestCase): ثم دوال test_.💡 تلميح 2
self.assertEqual(my_abs(-5), 5).حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
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)