اختبار الانحدار Regression
أصلحتَ شيئًا فكسرتَ آخر؛ كيف نضمن أن القديم ما زال يعمل؟ ⏱ نحو 40 دقيقة.
- تفهم الانحدار وكيف يحدث.
- تحرس السلوك القديم بإعادة تشغيل اختباراته.
- تضيف اختبارًا لكل عيبٍ تُصلحه.
- تعرف اختبار الدخان والانحدار المختار.
١العيب الذي يعود
أصلحت عيبًا، وأضفت ميزة — وفجأةً تعطّل شيءٌ كان يعمل! هذا الانحدار Regression: تغييرٌ جديد يكسر سلوكًا قديمًا. اختبار الانحدار هو إعادة تشغيل الاختبارات السابقة بعد كل تغيير للتأكّد أن ما كان يعمل ما زال يعمل.
هنا تظهر القيمة الكبرى للاختبارات الآلية: مجموعةٌ من مئات الاختبارات تُشغَّل في ثوانٍ بعد كل تعديل، فتكشف أيّ كسرٍ فورًا. بلا اختبارٍ آليّ، كل تغييرٍ مقامرة.
٢كيف يحدث الانحدار؟
انظر: دالةٌ تعمل، نضيف «ميزة» فتكسر حالةً قديمة. مجموعة الاختبارات تمسكها:
import unittest def price_after_tax(price): # الإصدار 2: أضفنا تقريبًا، لكنه كسر حالة الصفر! return round(price * 1.15, 2) or price * 1.15 class TestPrice(unittest.TestCase): def test_normal(self): self.assertEqual(price_after_tax(100), 115.0) def test_zero(self): self.assertEqual(price_after_tax(0), 0.0) # اختبار قديم يحرس الصفر import sysunittest.main(argv=[""], exit=False, testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))
الاختبار القديم test_zero هو حارس الانحدار: لو كسر تغييرٌ مستقبليّ حالة الصفر، لصرخ فورًا. كل عيبٍ تُصلحه، أضف اختبارًا يمنع عودته.
٣اختبار الدخان والاختيار
مجموعة الانحدار قد تكبر. استراتيجياتٌ عملية:
| الاستراتيجية | متى |
|---|---|
| اختبار الدخان Smoke | قبل كل شيء: حفنة اختباراتٍ للوظائف الحرجة (هل يعمل أصلًا؟) |
| انحدار كامل Full | قبل الإصدار الكبير: كل الاختبارات |
| انحدار مختار Selective | شغّل ما يمسّه التغيير فقط (أسرع) |
توقّع وتحقّق
عودة عيبٍ قديم بعد تغييرٍ جديد:
اختبار الانحدار:
حفنة اختباراتٍ للوظائف الحرجة:
عند إصلاح عيب:
الخلاصة
- الانحدار: تغييرٌ جديد يكسر سلوكًا كان يعمل.
- اختبار الانحدار يعيد تشغيل الاختبارات السابقة بعد كل تغيير.
- لكل عيبٍ تُصلحه اكتب اختبارًا يمنع عودته — حارسٌ دائم.
- اختبار الدخان للوظائف الحرجة بسرعة، والانحدار الكامل قبل الإصدار الكبير.
تحدٍّ برمجي: احرس ضدّ العودة
متقدمالدالة apply_coupon أُصلِحت لتتعامل مع القسيمة الفارغة. اكتب اختبارين يحرسان: قسيمة صحيحة تخصم 10، وقسيمة فارغة لا تخصم. شغّلهما.
- تحقّق أن
apply_coupon(100, "SAVE")==90. - تحقّق أن
apply_coupon(100, "")==100. شغّل.
ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:
import unittestdef apply_coupon(price, code): return price - 10 if code else price# اكتب اختبارين (قسيمة صحيحة وفارغة) ثم شغّلimport sys
💡 تلميح 1
test_valid: apply_coupon(100, "SAVE") == 90.💡 تلميح 2
test_empty: apply_coupon(100, "") == 100 يحرس العيب المُصلَح.حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import unittestdef apply_coupon(price, code): return price - 10 if code else priceclass TestCoupon(unittest.TestCase): def test_valid(self): self.assertEqual(apply_coupon(100, "SAVE"), 90) def test_empty(self): self.assertEqual(apply_coupon(100, ""), 100)import sysunittest.main(argv=[""], exit=False, testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))