التهيئة والتنظيف setUp و tearDown
كيف نبدأ كل اختبارٍ من صفحةٍ نظيفة، فلا يفسد اختبارٌ اختبارًا؟ ⏱ نحو 40 دقيقة.
- تستخدم
setUpللتهيئة قبل كل اختبار. - تستخدم
tearDownللتنظيف بعده. - تعزل الاختبارات فلا تتلوّث.
- تضمن نجاح الاختبارات بأيّ ترتيب.
١كل اختبارٍ يبدأ من صفحةٍ نظيفة
تخيّل عشرة اختباراتٍ لسلّة تسوّق، كلٌّ يحتاج سلّةً جديدة. تكرار إنشائها في كل دالةٍ ممل وخطر. الحلّ: setUp — دالةٌ يشغّلها الإطار قبل كل اختبار، فتبدأ من حالةٍ نظيفة معلومة:
import unittest class Cart: def __init__(self): self.items = [] def add(self, x): self.items.append(x) def total(self): return len(self.items) class TestCart(unittest.TestCase): def setUp(self): self.cart = Cart() # سلّة جديدة قبل كل اختبار def test_empty(self): self.assertEqual(self.cart.total(), 0) def test_add_one(self): self.cart.add("سماعة") self.assertEqual(self.cart.total(), 1) def test_add_two(self): self.cart.add("أ"); self.cart.add("ب") self.assertEqual(self.cart.total(), 2) import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)
كل اختبارٍ حصل على سلّةٍ فارغة خاصّة به. لو تشاركوا سلّةً واحدة، لأفسد اختبارُ الإضافة اختبارَ «الفارغة». عزل الاختبارات قاعدةٌ ذهبية.
٢tearDown: نظّف بعدك
بعض الاختبارات تفتح موردًا (ملفًّا، اتصالًا، بيانات مؤقّتة). tearDown يشغّله الإطار بعد كل اختبار لينظّف، حتى لو فشل الاختبار:
import unittest log = [] # نتظاهر بأنه مورد خارجيّ class TestResource(unittest.TestCase): def setUp(self): log.append("فتح") # قبل كل اختبار def tearDown(self): log.append("إغلاق") # بعد كل اختبار def test_a(self): self.assertTrue(True) def test_b(self): self.assertTrue(True) import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)print("سجلّ الدورة:", log)
لاحظ السجلّ: فتح/إغلاق لكل اختبار. tearDown يضمن ألّا يتسرّب مورد، فتبقى البيئة نظيفة للاختبار التالي.
٣القاعدة: اختبارات مستقلّة
هذا المبدأ (كل اختبارٍ مستقلّ ومعزول) هو ما يجعل مجموعة الاختبارات موثوقة: فشلٌ يعني عيبًا حقيقيًّا، لا تلوّثًا بين الاختبارات.
توقّع وتحقّق
setUp يُشغَّل:
tearDown يُشغَّل:
الاختبارات الجيّدة تنجح:
مشاركة حالة بين الاختبارات:
الخلاصة
setUpيُشغَّل قبل كل اختبار فيبدأ من حالةٍ نظيفة معلومة.tearDownيُشغَّل بعد كل اختبار لينظّف الموارد ولو فشل.- العزل قاعدةٌ ذهبية: كل اختبارٍ مستقلّ ينجح منفردًا وبأيّ ترتيب.
- مشاركة الحالة بين الاختبارات تجعلها هشّة وتخفي العيوب الحقيقية.
تحدٍّ برمجي: هيّئ قبل الاختبار
متوسطاكتب صنف اختبار لـ Stack يستخدم setUp لإنشاء مكدّس جديد، ثم اختبارين: الفارغ (الحجم 0) وبعد دفع عنصر (الحجم 1). شغّله.
- أنشئ المكدّس في
setUp. - اختبر الحجم 0، وبعد
pushالحجم 1، ثم شغّل.
ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:
import unittestclass Stack: def __init__(self): self.d = [] def push(self, x): self.d.append(x) def size(self): return len(self.d)# اكتب TestStack بـ setUp واختبارين ثم شغّلهimport sys
💡 تلميح 1
setUp: self.s = Stack().💡 تلميح 2
test_push: self.s.push(1) ثم assertEqual(self.s.size(), 1).حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import unittestclass Stack: def __init__(self): self.d = [] def push(self, x): self.d.append(x) def size(self): return len(self.d)class TestStack(unittest.TestCase): def setUp(self): self.s = Stack() def test_empty(self): self.assertEqual(self.s.size(), 0) def test_push(self): self.s.push(1) self.assertEqual(self.s.size(), 1)import sysrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)unittest.main(argv=[""], exit=False, testRunner=runner)