اختبارات Unittest والمجردة

بدلا من الانضمام


Unittest هو على الأرجح إطار كتابة الاختبار الأكثر شهرة في بيثون. من السهل جدًا التعلم وسهلة الاستخدام في مشروعك. ولكن لا يوجد شيء مثالي. في هذا المنشور أريد أن أتحدث عن ميزة واحدة أفتقدها شخصياً (على ما أظن ، ليست واحدة).

قليلا عن اختبارات الوحدة


قبل مناقشة (وإدانة) إطار الاختبار ، أرى أنه من الضروري التحدث قليلاً عن الاختبار بشكل عام. عندما سمعت للمرة الأولى عبارة "اختبار الوحدة" ، اعتقدت أن مسؤولية خدمة مراقبة الجودة هي التي تتحقق من وحدات البرامج للتأكد من توافقها مع المتطلبات. تخيل دهشتي عندما اكتشفت أن هذه الاختبارات نفسها يجب أن يكتبها المبرمجون. في البداية لم أكتب اختبارات ... على الإطلاق. لا شيء يمكن أن يحل محل هذه الأحاسيس عندما تستيقظ في الصباح وتقرأ من المستخدم "البرنامج لا يعمل. هناك حاجة ماسة لعمل شيء ما. " في البداية بدا لي أن هذه كانت عملية طبيعية تمامًا ، حتى كتبت اختبار الوحدة الأول ، والذي وجد خطأً فيه. اختبارات الوحدة عمومًا تبدو عديمة الفائدة تمامًا حتى تكتشف المشكلات. الآن لا يمكنني الالتزام بوظيفة جديدة دون كتابة اختبار عليها.
لكن الاختبارات لا تستخدم فقط للتحقق من أن الكود مكتوب بشكل صحيح. فيما يلي قائمة بالوظائف التي ، في رأيي ، تقوم بإجراء الاختبارات:

  • الكشف عن الأخطاء في كود البرنامج
  • إعطاء المبرمجين الثقة التي تعمل التعليمات البرمجية الخاصة بهم
  • النتيجة الطبيعية من الفقرة السابقة: القدرة على تعديل البرنامج بأمان
  • الاختبارات - نوع من الوثائق التي تصف بشكل صحيح سلوك النظام

بمعنى ما ، تكرر الاختبارات هيكل البرنامج. هل مبادئ بناء البرامج قابلة للتطبيق على الاختبارات؟ أعتقد أن نعم - هذا هو نفس البرنامج ، وإن كان اختبار برنامج آخر.

وصف المشكلة


في مرحلة ما ، حصلت على فكرة كتابة اختبار تجريبي. ماذا اقصد بذلك؟ هذا اختبار لا ينفذ نفسه ، ولكنه يعلن عن الأساليب التي تعتمد على المعلمات المحددة في الورثة. ثم اكتشفت أنه لا يمكنني القيام بذلك بشكل إنساني في أحر. هنا مثال:

class SerializerChecker(TestCase): model = None serializer = None def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props) 

أعتقد أنه حتى بدون معرفة تنفيذ TestObjectFactory وأسلوب check_dict ، فمن الواضح أن الدعائم هي قاموس لخصائص كائن ما ، فإن obj عبارة عن كائن نقوم بفحصه من المسلسل. check_dict يتحقق بشكل متكرر من القواميس لمباراة. أعتقد أن العديد من الأشخاص المطلعين على أداؤهم سيقولون على الفور أن هذا الاختبار لا يلبي تعريفي للملخص. لماذا؟ لأن طريقة test_fields_creation سيتم تنفيذها من هذه الفئة ، والتي لا نحتاجها على الإطلاق. بعد البحث عن المعلومات ، توصلت إلى استنتاج مفاده أن الخيار الأكثر ملاءمة هو عدم توريث SerializerChecker من TestCase ، ولكن لتنفيذ الورثة بطريقة أو بأخرى:

 class VehicleSerializerTest(SerializerChecker, RecursiveTestCase): model = Vehicle serializer = VehicleSerialize 

RecursiveTestCase هو سليل TestCase الذي ينفذ أسلوب check_dict.

هذا الحل قبيح على الفور من عدة مواقع:

  • في فئة SerializerChecker ، نحتاج إلى معرفة أن الطفل يجب أن يرث من TestCase. يمكن أن يسبب هذا التبعية مشاكل لأولئك الذين لا يعرفون هذا الرمز.
  • تعتقد بيئة التطوير بعناد أنني مخطئ ، نظرًا لأن SerializerChecker ليس لديه طريقة check_dict

صورة

خطأ أن بيئة التطوير يلقي

قد يبدو أنه يمكنك ببساطة إضافة كعب روتين لـ check_dict وتم حل جميع المشكلات:

 class SerializerChecker: model = None serializer = None def check_dict(self, data, props): raise NotImplementedError def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props) 

لكن هذا ليس حلاً كاملاً للمشكلة:

  • في الواقع ، أنشأنا واجهة لا تنفذ سليل من هذه الفئة ، ولكن RecursiveTestCase ، مما يخلق أسئلة معقولة للهندسة المعمارية.
  • يحتوي TestCase على العديد من أساليب تأكيد *. هل نحن بحاجة حقًا لكتابة كعب لكل مستخدم؟ لا يزال يبدو وكأنه حل جيد؟

لتلخيص


لا يوفر Unittest القدرة عاقل "فصل" فئة موروثة من TestCase. سأكون سعيدًا جدًا إذا تمت إضافة هذه الوظيفة إلى الإطار. كيف يمكنك حل هذه المشكلة؟

Source: https://habr.com/ru/post/ar447438/


All Articles