الاختبارات المَعلَمة
عشر حالاتٍ باختبارٍ واحد نظيف، وكل فشلٍ يُبلَّغ بدقّة. ⏱ نحو 40 دقيقة.
- تكتب اختبارًا مَعلَمًا بجدول حالات.
- تستخدم
subTestلتبليغ كل فشل. - تميّز متى تعلّم ومتى تفصل.
- تعرف مكافئ
pytest.
١اختبارٌ واحد، حالاتٌ كثيرة
لدينا دالةٌ نريد اختبارها على عشر حالات. أنكتب عشر دوال test_ متطابقة إلا في الأرقام؟ تكرارٌ ممل. الحلّ: اختبار مَعلَم Parametrized — جدول حالاتٍ نمرّ عليه، وكل حالةٍ تُبلَّغ منفصلة بـ subTest:
import unittest def classify(score): if score >= 90: return "ممتاز" if score >= 60: return "ناجح" return "راسب" class TestClassify(unittest.TestCase): def test_all(self): cases = [(95, "ممتاز"), (90, "ممتاز"), (75, "ناجح"), (60, "ناجح"), (59, "راسب"), (0, "راسب")] for score, expected in cases: with self.subTest(score=score): self.assertEqual(classify(score), expected) import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)
ميزة subTest: لو فشلت حالةٌ، يستمرّ الإطار ويبلّغ كل الحالات الفاشلة، لا الأولى فقط. ويذكر قيمة score التي فشلت، فتعرف أيّها بالضبط.
٢قوّة subTest: كشف كل الأعطال
انظر دالةً فيها عيبٌ عند حدٍّ واحد، وكيف يحدّد الاختبار المَعلَم الحالة الفاشلة بدقّة:
import unittest def is_adult(age): return age > 18 # ✗ عيب: يجب >= 18 class TestAdult(unittest.TestCase): def test_boundary(self): cases = [(17, False), (18, True), (19, True), (25, True)] for age, expected in cases: with self.subTest(age=age): self.assertEqual(is_adult(age), expected) import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)
التقرير يشير إلى (age=18): الحالة الوحيدة الفاشلة. لولا subTest لعرفنا أن «شيئًا ما» فشل دون تحديده. هذا يسرّع التشخيص كثيرًا.
٣متى نعلّم الاختبار؟
في العمل الحقيقيّ تقدّم أطرٌ مثل pytest تعليمًا أوجز بـ @pytest.mark.parametrize، لكن الفكرة واحدة: جدولُ (مدخل، متوقَّع) يُشغَّل آليًّا.
توقّع وتحقّق
الاختبار المَعلَم يفيد حين:
subTest يفيد في:
بلا subTest، حلقة الاختبار:
في pytest التعليم بـ:
الخلاصة
- الاختبار المَعلَم يشغّل جدول حالاتٍ بدل تكرار دوالٍ متطابقة.
subTestيبلّغ كل الحالات الفاشلة مع قيمها، لا الأولى فقط.- نعلّم الحالات المتشابهة سلوكيًّا، ونفصل السلوكيات المختلفة.
pytestيقدّم المبدأ نفسه بـ @parametrizeالأوجز.
تحدٍّ برمجي: اختبار مَعلَم
متوسطاكتب اختبارًا مَعلَمًا لدالة sign (تعيد "+", "-", "0") على الحالات المعطاة باستخدام subTest، ثم شغّله.
- مرّ على الحالات داخل
with self.subTest(...). - استخدم
assertEqual، ثم شغّل.
ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:
import unittestdef sign(n): if n > 0: return "+" if n < 0: return "-" return "0"# اكتب اختبارًا مَعلَمًا على: (5,"+"),(-3,"-"),(0,"0") ثم شغّلهimport sys
💡 تلميح 1
for n, expected in [...]: with self.subTest(n=n):💡 تلميح 2
self.assertEqual(sign(n), expected).حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import unittestdef sign(n): if n > 0: return "+" if n < 0: return "-" return "0"class TestSign(unittest.TestCase): def test_all(self): for n, expected in [(5, "+"), (-3, "-"), (0, "0")]: with self.subTest(n=n): self.assertEqual(sign(n), expected)import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)