مشروع: خطة اختبار كاملة وتنفيذها
من خطّة الاختبار إلى قرار الإطلاق: المقرر كلّه في وحدةٍ واحدة. ⏱ نحو 70 دقيقة.
- تضع خطة اختبارٍ كاملة لوحدة.
- تصمّم حالاتٍ تغطّي كل الفئات.
- تنفّذ المجموعة وتقيس التغطية.
- تقرّر الجاهزية بمعايير الخروج.
١المقرر كلّه في مشروع
هذا المقرر في مشروعٍ واحد: نضع خطة اختبار لوحدة «السلّة» في متجر ويب، ثم ننفّذها بالأدوات التي تعلّمناها — حالاتٌ صالحة وفاسدة وحدّية، واختبار استثناءات، وتغطية، وقرار إطلاق.
٢1) الوحدة تحت الاختبار + خطة الحالات
الوحدة: دالة cart_total تجمع الأصناف وتطبّق الشحن والخصم. خطة الحالات تغطّي الفئات:
| الفئة | الحالة | المتوقَّع |
|---|---|---|
| صحيح | سلّة 100 دون خصم | 115 (شحن 15) |
| حدّ | سلّة 200 (عتبة الشحن المجّانيّ) | 200 |
| خصم | سلّة 100 بقسيمة 10% | 90 + شحن 15 = 105 |
| خطأ | كمية سالبة | يرفع ValueError |
٣2+3) التنفيذ: مجموعة اختبارٍ كاملة
import unittest def cart_total(subtotal, discount_pct=0): if subtotal < 0: raise ValueError("المجموع سالب") after_discount = subtotal * (1 - discount_pct / 100) shipping = 0 if after_discount >= 200 else 15 return round(after_discount + shipping, 2) class TestCart(unittest.TestCase): def test_normal(self): # صحيح self.assertEqual(cart_total(100), 115) def test_free_shipping_boundary(self): # حدّ self.assertEqual(cart_total(200), 200) def test_with_discount(self): # خصم self.assertEqual(cart_total(100, 10), 105) def test_negative_raises(self): # خطأ with self.assertRaises(ValueError): cart_total(-50) import sysunittest.main(argv=[""], exit=False, testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))
أربع حالاتٍ غطّت الفئات الأربع: صحيح، حدّ، خصم، وخطأ. OK تعني أن الوحدة تحقّق خطّتها.
٤4+5) التغطية وقرار الإطلاق
# قياس مبسّط لتغطية الفروع، ثم قرار الخروجbranches = {"full_shipping", "free_shipping", "discount", "error"}covered = {"full_shipping", "free_shipping", "discount", "error"} # حالاتنا لمستها كلّهاcoverage = len(covered) / len(branches) * 100print("تغطية الفروع:", coverage, "%") def ready(critical_bugs, coverage, all_pass): return critical_bugs == 0 and coverage >= 85 and all_pass decision = ready(critical_bugs=0, coverage=coverage, all_pass=True)print("جاهز للإطلاق؟", decision)print("القرار موثّق: لا عيوب حرجة، تغطية", int(coverage), "%، كل الاختبارات خضراء")
اكتمل المشروع: خطةٌ، فحالاتٌ تغطّي الفئات، فتنفيذٌ أخضر، فتغطيةٌ كاملة، فقرار إطلاقٍ مبنيٍّ على معايير. هذا مسار ضمان الجودة الذي بدأناه بـ«كلفة العيب» وأنهيناه بـ«قرارٍ موثّق».
توقّع وتحقّق
أول خطوة في خطة الاختبار:
خطة الحالات تغطّي:
قرار الإطلاق يُبنى على:
آخر خطوة بعد التنفيذ:
الخلاصة
- المشروع يجمع المقرر: خطّط ← صمّم الحالات ← نفّذ ← قِس التغطية ← قرّر.
- غطِّ الفئات الأربع: صحيح، خطأ، حدّ، واستثناء.
- اجعل الاختبارات مستقلّة وقِس التغطية لتكشف ما لم يُختبَر.
- قرار الإطلاق مبنيٌّ على معايير خروجٍ موثّقة لا على الشعور.
تحدٍّ برمجي: المشروع الختامي
متقدماكتب مجموعة اختبارٍ كاملة لدالة final_price (سعر مع خصم): حالة صحيحة، حالة خصم، وحالة خطأ (سعر سالب يرفع ValueError). شغّلها.
- اختبر:
final_price(100)==100، وfinal_price(100,20)==80، وأنfinal_price(-5)يرفع ValueError. - شغّل المجموعة.
ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:
import unittestdef final_price(price, discount=0): if price < 0: raise ValueError("سعر سالب") return round(price * (1 - discount / 100), 2)# اكتب TestFinal بثلاث حالات (صحيح، خصم، خطأ) ثم شغّلimport sys
💡 تلميح 1
test_normal, test_discount, test_negative.💡 تلميح 2
with self.assertRaises(ValueError): final_price(-5).حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import unittestdef final_price(price, discount=0): if price < 0: raise ValueError("سعر سالب") return round(price * (1 - discount / 100), 2)class TestFinal(unittest.TestCase): def test_normal(self): self.assertEqual(final_price(100), 100) def test_discount(self): self.assertEqual(final_price(100, 20), 80) def test_negative(self): with self.assertRaises(ValueError): final_price(-5)import sysunittest.main(argv=[""], exit=False, testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))