بايثون / برامج موثوقة / الدرس 9
اختبار العقود بالمكتبة القياسية
ابن اختبارات صغيرة محددة للسلوك الملحوظ تشمل الحدود والفشل المتوقع، بدلا من اختبار تفاصيل التنفيذ العرضية.
في هذه الصفحة
أهداف الدرس
- الاختبار يقدم ادعاء
- ضبط الاعتماديات وتفسير النتائج
الاختبار يقدم ادعاء
يقدم الاختبار مدخلات مضبوطة وينفذ العملية ويقارن النتيجة بتوقع مبرر. يجب اشتقاق المتوقع من المتطلب أو برهان مستقل، لا من نسخ التنفيذ نفسه. تحتاج دالة أصغر عددين إلى حالتي التساوي والسالب؛ قد تفوتهما أمثلة عشوائية. توثق الاختبارات الثقة ضمن نطاقها ولا تثبت صحة كل تنفيذ ممكن. ابدأ بجملة تصف السلوك، ثم اختر أصغر حالة تفشل إذا انكسر ذلك السلوك.
تقسيم مجال المدخلات
قسم المدخلات إلى فئات متشابهة السلوك واختبر الحدود بينها. يحتاج محلل الكمية إلى موجب وصفر وسالب ونص تالف وربما فراغات بحسب العقد. وللمجموعات اختبر الفراغ وعنصرا واحدا والتكرار وعدة عناصر. اجعل الاستثناء المتوقع جزءا من السلوك، ولا تعتبر أي فشل نجاحا. ليكن لكل اختبار تركيز واسم يوضح الوعد المنكسر. قد يخفي سيناريو ضخم بافتراضات كثيرة أول سبب لم يعد صحيحا.
استخدام unittest عن قصد
توفر unittest تأكيدات TestCase ومشغلا. تبدأ أسماء طرائق الاختبار بـ test، وتصف التأكيدات النتيجة المطلوبة. تستطيع setUp إنشاء تجهيز جديد لكل اختبار، وتحمي المجلدات المؤقتة بيانات المشروع الحقيقية. يعرف المثال محللا صغيرا ويختبر النجاح والرفض ثم يطبع ملخصا ثابتا. تتحقق assertRaises من نوع الاستثناء، ويمكن لنسختها ذات التعبير المنتظم فحص الرسالة. لا تؤكد التتبع كاملا لأن أسماء الملفات والأسطر تفاصيل تتغير مع الشفرة.
import io
import unittest
def positive(text):
value = int(text)
if value <= 0:
raise ValueError("must be positive")
return value
class ParserTests(unittest.TestCase):
def test_valid(self):
self.assertEqual(positive("4"), 4)
def test_zero(self):
with self.assertRaisesRegex(ValueError, "positive"):
positive("0")
result = unittest.TextTestRunner(stream=io.StringIO()).run(
unittest.defaultTestLoader.loadTestsFromTestCase(ParserTests)
)
print(result.testsRun, result.wasSuccessful())
2 True
يفحص الاختباران سلوكين مستقلين. يمنع التقاط خرج المشغل طباعة الوقت المتغير، مع الاحتفاظ بالنتيجة الحقيقية.
فحص العلاقات إلى جانب الإجابات
قد تربط خاصية بين المدخل والنتيجة بصورة أعم من عدد واحد. يجب أن يحفظ الترتيب العناصر ويجعلها غير متناقصة، ويجب أن يعيد فصل الدقائق بناء الأصل. يفحص المثال الثاني إعادة البناء ومجال الباقي عند حدود باستخدام subTest. إنها حالات محددة وليست برهانا شاملا. قد تكون الخاصية ضعيفة؛ ففحص الترتيب وحده يقبل قائمة فارغة فقدت البيانات. اجمع خصائص مستقلة مع نتائج صريحة كي لا تختبر نصف العقد فقط.
import io
import unittest
class SplitTests(unittest.TestCase):
def test_reconstruction(self):
for total in [0, 59, 60, 137]:
with self.subTest(total=total):
hours, minutes = divmod(total, 60)
self.assertEqual(hours * 60 + minutes, total)
self.assertTrue(0 <= minutes < 60)
result = unittest.TextTestRunner(stream=io.StringIO()).run(
unittest.defaultTestLoader.loadTestsFromTestCase(SplitTests)
)
print(result.wasSuccessful())
True
يجب أن تحقق كل حالة إعادة البناء ومجال الباقي معا. تربط subTest فشل التأكيد بالمدخل الذي سببه.
ضبط الاعتماديات وتفسير النتائج
لا يعتمد اختبار الوحدة المحدد على شبكة حية أو توقيت الساعة أو ترتيب ملفات عشوائي أو حالة تركها اختبار سابق. مرر الساعة أو مصدر البيانات كاعتمادية عند الحاجة. تستطيع unittest.mock استبدال متعاون في موضع البحث عنه، لكن الإفراط قد يختبر تفاعلا خياليا. احتفظ باختبارات تكامل للحدود المهمة مثل تحويل JSON الحقيقي في مجلد مؤقت. عند إصلاح عيب تحقق من فشل حالة التراجع قبل الإصلاح ونجاحها بعده. تبين التغطية المسارات المنفذة ولا تقيس جودة التأكيدات.
الأخطاء الشائعة وإصلاحها
نجاح الاختبار بعد فساد التنفيذ
- لماذا يحدث؟
- تكرر النتيجة المتوقعة الخوارزمية المعيبة.
- كيف تصلحه؟
- اشتق الإجابة بصورة مستقلة من مثال صغير معروف.
- كيف تتجنب تكراره؟
- اسأل أي عيب معقول يكشفه كل تأكيد.
فشل الاختبارات عند تشغيلها معا فقط
- لماذا يحدث؟
- تتشاطر تجهيزات قابلة للتعديل أو حالة متسربة.
- كيف تصلحه؟
- أنشئ تجهيزات جديدة ونظف الموارد في كل اختبار.
- كيف تتجنب تكراره؟
- شغّل الاختبارات المتأثرة منفردة ومجتمعة لفحص العزل.
فشل تأكيد زمني على جهاز أبطأ
- لماذا يحدث؟
- خلط الاختبار السلوك الوظيفي بحد زمني متغير.
- كيف تصلحه؟
- اختبر النتائج بصورة محددة وقس الأداء منفصلا.
- كيف تتجنب تكراره؟
- تجنب حدود الزمن الثابتة في اختبارات الوحدة العادية.
اختبر فهمك
1. اختر أربع حالات لدالة تقبل درجات صحيحة من صفر إلى مئة.
اعرض الحل المشروح
اختبر صفر ومئة بوصفهما حدين مقبولين، وسالب واحد ومئة وواحد بوصفهما جارَين مرفوضين. أضف قيمة داخلية ونوعا غير صحيح إذا شمل العقد التحقق من النوع. تميز الحدود الشاملة من خطأ زيادة أو نقصان واحد.
2. يفحص اختبار ترتيب output == sorted(output) وحدها. أي عيب مهم قد يفوته؟
اعرض الحل المشروح
ينجح تنفيذ يعيد قائمة فارغة دائما لأنها مرتبة. أكد أيضا حفظ العناصر وتكراراتها، وأضف نتيجة متوقعة صريحة لمدخل صغير يحتوي تكرارا.
أهم ما تعلمته
- اختبر الوعود الملحوظة وحالات الحدود.
- تكشف التوقعات المستقلة الأخطاء المنسوخة.
- يجعل العزل والمدخل المحدد الفشل قابلا للتشخيص.