المقدمة: تلقت الجامعة مهمة - لتجميع فريق scrum ، واختيار مشروع والعمل عليه لفصل دراسي. اختار فريقنا تطوير تطبيق الويب (رد فعل + قارورة). سأحاول في هذه المقالة إخبارك عن الاختبارات التي يجب أن تكون وتحليل ما قمنا به على الواجهة الخلفية.

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

اختبارات الوحدة
تتوافق اختبارات الوحدة مع الاختبارات الصغيرة - يجب أن تكون سريعة وفحص صحة أجزاء معينة من البرنامج فقط. لا ينبغي لهم الوصول إلى قاعدة البيانات ، ويجب ألا يعملوا في بيئات معقدة متعددة الخيوط. يتحكمون في الامتثال للمواصفات / المعايير ، وغالبًا ما يكون لديهم دور اختبارات الانحدار .
اختبارات التكامل
اختبارات التكامل هي تلك الاختبارات التي قد تؤثر على عدة وحدات ووظائف. تتطلب مثل هذه الاختبارات مزيدًا من الوقت وقد تتطلب بيئات خاصة. وهي ضرورية للتأكد من أن الوحدات والوظائف الفردية يمكن أن تعمل مع بعضها البعض. أي تحقق اختبارات الوحدة من مطابقة الواجهات الحقيقية للاختبارات المتوقعة ، والتكامل - أن الوظائف والوحدات النمطية تتفاعل بشكل صحيح مع بعضها البعض.
اختبارات النظام
هذا هو أعلى مستوى من الاختبار التلقائي. تحقق اختبارات النظام من أن النظام بأكمله يعمل وأن أجزائه تؤدي مهامها وتكون قادرة على التفاعل بشكل صحيح.
لماذا تتبع الأنواع
عادة ، مع نمو المشروع ، ستنمو قاعدة الكود أيضًا. ستزداد مدة الاختبارات التلقائية ؛ وسيصبح دعم عدد كبير من اختبارات التكامل والنظام أكثر صعوبة. لذلك ، فإن التحدي للمطورين هو تقليل الاختبارات اللازمة. للقيام بذلك ، يجب أن تحاول استخدام اختبارات الوحدة حيثما أمكن ذلك وتقليل التكامل باستخدام "mocks" (mocks).
واقع
اختبار API نموذجي
def test_user_reg(client): return json.loads( client.post(url, json=data, content_type='application/json').data ) response = client.post('api/user.reg', json={ 'email': 'name@mail.ru', 'password': 'password1', 'first_name': 'Name', 'last_name': 'Last Name' }) data = json.loads(response.data) assert data['code'] == 0
من الوثائق الرسمية للقارورة ، نحصل على وصفة جاهزة لتهيئة التطبيق وإنشاء قاعدة البيانات. هنا هو العمل مع قاعدة البيانات. هذا ليس اختبار وحدة ، ولكن ليس اختبار نظام. هذا اختبار تكامل يستخدم تطبيق اختبار قاعدة البيانات.
لماذا التكامل بدلا من وحدات؟ لأنه في معالجة الاستعلام ، يتم التفاعل مع قارورة ، مع ORM ، مع منطق أعمالنا. يتصرف المعالجون كعنصر موحد لأجزاء أخرى من المشروع ، لذا فإن كتابة اختبارات الوحدة لهم ليست سهلة للغاية (تحتاج إلى استبدال قاعدة البيانات بالمهارات والمنطق الداخلي) وليست عملية للغاية (اختبارات التكامل ستفحص الجوانب المتشابهة - "هل كانت الوظائف اللازمة تسمى؟" ، " هل تم استلام البيانات بشكل صحيح؟ "، إلخ).
أسماء وتجميع الاختبارات
def test_not_empty_errors(): assert validate_not_empty('email', '') == ('email is empty',) assert validate_not_empty('email', ' ') == ('email is empty',) assert validate_email_format('email', "") == ('email is empty',) assert validate_password_format('pass', "") == ('pass is empty',) assert validate_datetime('datetime', "") == ('datetime is empty',)
في هذا الاختبار ، يتم استيفاء جميع الشروط للاختبارات "الصغيرة" - يتم فحص سلوك الوظيفة دون التبعيات للتأكد من توافقها مع المتوقع. لكن التصميم يثير أسئلة.
من الممارسات الجيدة كتابة الاختبارات التي تركز على جانب معين من البرنامج. في هذا المثال ، هناك وظائف مختلفة - validate_password_format
، validate_password_format
، validate_datetime
. لا تعتمد عمليات تجميع التجميع على النتيجة ، ولكن على كائنات الاختبار.
لا يصف اسم الاختبار ( test_not_empty_errors
) كائن الاختبار (الطريقة التي يتم اختبارها) ، فهو يصف النتيجة فقط (الأخطاء ليست فارغة). يجب أن تسمى هذه الطريقة test__validate_not_empty__error_on_empty
. يصف هذا الاسم ما يتم اختباره والنتيجة المتوقعة. ينطبق هذا على كل اسم اختبار تقريبًا في المشروع نظرًا لحقيقة أنه لم يتم تخصيص وقت لمناقشة اصطلاحات تسمية الاختبار.
اختبارات الانحدار
def test_datetime_errors(): assert validate_datetime('datetime', '0123-24-31T;431') == ('datetime is invalid',) assert validate_datetime('datetime', '2018-10-18T20:21:21+-23:1') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-13-20T20:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-02-29T20:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T25:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:61:20+22:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:61+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:20+25:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:20+20:61') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-13-35T25:61:61+61:61') == ('datetime is invalid',)
تألف هذا الاختبار في الأصل من assert
الأولين. بعد ذلك ، تم اكتشاف "خطأ" - بدلاً من التحقق من التاريخ ، تم التحقق من التعبير العادي فقط ، أي 9999-99-99
كان يعتبر التاريخ الطبيعي. المطور إصلاحه. بطبيعة الحال ، بعد إصلاح الخلل ، تحتاج إلى إضافة اختبارات لمنع الانحدار في المستقبل. بدلاً من إضافة اختبار جديد لكتابة سبب وجود هذا الاختبار ، تمت إضافة الاختبارات إلى هذا الاختبار.
ما الذي يجب استدعاء اختبار جديد لإضافة التحقق منه؟ ربما test__validate_datetime__error_on_bad_datetime
.
تجاهل الأدوات
def test_get_providers(): class Tmp: def __init__(self, id_external, token, username): self.id_external = id_external self.token = token self.username = username ...
Tmp
؟ هذا بديل لكائن غير مستخدم في هذا الاختبار. لا يبدو المطور على علم بوجود @patch
و MagicMock
من unittest.mock
. لا حاجة لتعقيد الكود عن طريق حل المشكلات بسذاجة عندما تكون هناك أدوات أكثر ملائمة.
هناك مثل هذا الاختبار الذي تهيئة الخدمات (في قاعدة البيانات) ، يستخدم سياق التطبيق.
def test_get_posts(client): def fake_request(*args, **kwargs): return [one, two] handler = VKServiceHandler() handler.request = fake_request services_init() with app.app_context(): posts = handler.get_posts(None) assert len(posts) == 2
يمكنك استبعاد قاعدة البيانات والسياق من الاختبار عن طريق إضافة واحدة @patch
واحدة.
@patch("mobius.services.service_vk.Service") def test_get_posts(mock): def fake_request(*args, **kwargs): return [one, two] handler = VKServiceHandler() handler.request = fake_request posts = handler.get_posts(None) assert len(posts) == 2
النتائج
- لتطوير البرمجيات عالية الجودة ، تحتاج إلى كتابة الاختبارات. كحد أدنى ، تأكد من كتابة ما تحتاجه.
- بالنسبة لأنظمة المعلومات الكبيرة ، تعتبر الاختبارات أكثر أهمية - فهي تسمح لك بتجنب تغييرات الواجهة غير المرغوب فيها أو إرجاع الأخطاء.
- حتى لا تتحول الاختبارات المكتوبة إلى الكثير من الأساليب الغريبة مع مرور الوقت ، يجب عليك الانتباه إلى اصطلاح تسمية الاختبارات ، والالتزام بالممارسات الجيدة ، وتقليل الاختبارات.
- يمكن أن تكون اختبارات الوحدة أداة رائعة أثناء التطوير. يمكن تشغيلها بعد كل تغيير صغير للتأكد من عدم كسر أي شيء.
نقطة مهمة للغاية هي أن الاختبارات لا تضمن توافر أو عدم وجود أخطاء. تضمن الاختبارات أن تكون النتيجة الحقيقية للبرنامج (أو جزء منه) متوقعة. في هذه الحالة ، يحدث التحقق فقط لتلك الجوانب التي تمت كتابة الاختبارات عليها. لذلك ، عند إنشاء منتج عالي الجودة ، يجب ألا ننسى أنواع الاختبارات الأخرى.