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

مشروع: خطة اختبار كاملة وتنفيذها

من خطّة الاختبار إلى قرار الإطلاق: المقرر كلّه في وحدةٍ واحدة. ⏱ نحو 70 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تضع خطة اختبارٍ كاملة لوحدة.
  • تصمّم حالاتٍ تغطّي كل الفئات.
  • تنفّذ المجموعة وتقيس التغطية.
  • تقرّر الجاهزية بمعايير الخروج.

١المقرر كلّه في مشروع

هذا المقرر في مشروعٍ واحد: نضع خطة اختبار لوحدة «السلّة» في متجر ويب، ثم ننفّذها بالأدوات التي تعلّمناها — حالاتٌ صالحة وفاسدة وحدّية، واختبار استثناءات، وتغطية، وقرار إطلاق.

٢1) الوحدة تحت الاختبار + خطة الحالات

الوحدة: دالة cart_total تجمع الأصناف وتطبّق الشحن والخصم. خطة الحالات تغطّي الفئات:

الفئةالحالةالمتوقَّع
صحيحسلّة 100 دون خصم115 (شحن 15)
حدّسلّة 200 (عتبة الشحن المجّانيّ)200
خصمسلّة 100 بقسيمة 10%90 + شحن 15 = 105
خطأكمية سالبةيرفع ValueError

٣2+3) التنفيذ: مجموعة اختبارٍ كاملة

main.py
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))
Shell×
>>> %Run main.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
NO TESTS RAN
>>>

أربع حالاتٍ غطّت الفئات الأربع: صحيح، حدّ، خصم، وخطأ. OK تعني أن الوحدة تحقّق خطّتها.

٤4+5) التغطية وقرار الإطلاق

main.py
# قياس مبسّط لتغطية الفروع، ثم قرار الخروج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), "%، كل الاختبارات خضراء")
Shell×
>>> %Run main.py
تغطية الفروع: 100.0 %
جاهز للإطلاق؟ True
القرار موثّق: لا عيوب حرجة، تغطية 100 %، كل الاختبارات خضراء
>>>

اكتمل المشروع: خطةٌ، فحالاتٌ تغطّي الفئات، فتنفيذٌ أخضر، فتغطيةٌ كاملة، فقرار إطلاقٍ مبنيٍّ على معايير. هذا مسار ضمان الجودة الذي بدأناه بـ«كلفة العيب» وأنهيناه بـ«قرارٍ موثّق».

توقّع وتحقّق

أول خطوة في خطة الاختبار:

خطة الحالات تغطّي:

قرار الإطلاق يُبنى على:

آخر خطوة بعد التنفيذ:

الخلاصة

تحدٍّ برمجي: المشروع الختامي

متقدم

اكتب مجموعة اختبارٍ كاملة لدالة final_price (سعر مع خصم): حالة صحيحة، حالة خصم، وحالة خطأ (سعر سالب يرفع ValueError). شغّلها.

المطلوب:
  • اختبر: final_price(100)==100، وfinal_price(100,20)==80، وأن final_price(-5) يرفع ValueError.
  • شغّل المجموعة.

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

main.py
import unittestdef final_price(price, discount=0):    if price < 0:        raise ValueError("سعر سالب")    return round(price * (1 - discount / 100), 2)# اكتب TestFinal بثلاث حالات (صحيح، خصم، خطأ) ثم شغّلimport sys
المطلوب×
----------------------------------------------------------------------
Ran 0 tests in 0.000s
NO TESTS RAN
💡 تلميح 1
ثلاث دوال: test_normal, test_discount, test_negative.
💡 تلميح 2
للخطأ: with self.assertRaises(ValueError): final_price(-5).

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

main.py
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))
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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