في مجال الاختبار التلقائي ، يمكنك العثور على أدوات متنوعة ، على سبيل المثال ، py.test هو أحد أكثر الحلول شعبية لكتابة الاختبارات التلقائية في Python.
بعد الاطلاع على الكثير من الموارد المتعلقة بـ pytest ودراسة الوثائق من الموقع الرسمي للمشروع ، لم أتمكن من العثور على وصف مباشر للحل لأحد المهام الرئيسية - تشغيل الاختبارات مع اختبار البيانات المخزنة في ملف منفصل. خلاف ذلك ، يمكن القول ، تحميل المعلمات في وظائف الاختبار من الملف (الملفات) أو المعلمة من الملف مباشرة. لم يتم وصف هذا الإجراء في أي مكان في التعقيدات والإشارة الوحيدة لهذه الميزة موجودة في سطر واحد فقط من وثائق pytest.
في هذه المقالة سوف أتحدث عن حل لهذه المشكلة.
مهمة
وتتمثل المهمة الرئيسية في إنشاء حالات اختبار في شكل معلمات test_input
و expected_result
في كل وظيفة اختبار فردية من أسماء وظائف الملف المقابلة.
مهام إضافية:
- اختيار تنسيق قابل للقراءة من قبل الإنسان للملفات مع حالات اختبار ؛
- ترك القدرة على دعم الحالات اختبار الثابت ترميز ؛
- عرض معرفات واضحة لكل حالة.
أدوات
في المقالة ، أستخدم Python 3 (2.7 مناسب أيضًا) و pyyaml و pytest
(الإصدارات 5+ لـ Python 3 أو 4.6 لـ Python 2.7) دون استخدام الإضافات الخارجية. بالإضافة إلى ذلك ، سيتم استخدام مكتبة os
القياسية.
يجب هيكلة الملف نفسه الذي سنأخذ منه حالات الاختبار باستخدام لغة ترميز ملائمة لفهم الشخص. في حالتي ، تم اختيار YAML (لأنه يحل المهمة الإضافية المتمثلة في اختيار تنسيق قابل للقراءة البشرية) . في الواقع ، يعتمد نوع لغة الترميز للملفات التي تحتوي على مجموعات بيانات على المتطلبات المقدمة في المشروع.
تطبيق
نظرًا لأن الدعامة الرئيسية للكون في البرمجة هي الاتفاق ، فسيتعين علينا تقديم العديد منها لحلنا.
اعتراض
بادئ ذي بدء ، يستخدم هذا الحل وظيفة اعتراض pytest_generate_tests
( wiki ) ، والتي تبدأ في مرحلة إنشاء حالات الاختبار ، و metafunc
للوسيطة الخاصة metafunc
، والتي تتيح لنا تحديد معلمة الوظيفة. في هذه المرحلة ، يتكرر pytest على كل وظيفة اختبار وتنفيذ شفرة التوليد اللاحقة لها.
الحجج
يجب عليك تحديد قائمة شاملة من المعلمات لوظائف الاختبار. في حالتي ، يكون القاموس test_input
وأي نوع بيانات (غالبًا ما يكون سلسلة أو عددًا صحيحًا) في expected_result
. نحن بحاجة إلى هذه المعلمات للاستخدام في metafunc.parametrize(...)
.
البارامترات
تكرر هذه الوظيفة تمامًا عملية تثبيت المعلمات @pytest.mark.parametrize
، والتي تأخذ كحجة أولى سلسلة تسرد وسيطات دالة الاختبار (في حالتنا "test_input, expected_result"
) وقائمة من البيانات التي ستعمل من خلالها على إنشاء حالات الاختبار الخاصة بنا (على سبيل المثال ، [(1, 2), (2, 4), (3, 6)]
).
في المعركة ، سيبدو كما يلي:
@pytest.mark.parametrize("test_input, expected_result", [(1, 2), (2, 4), (3, 6)]) def test_multiplication(test_input, expected_result): assert test_input * 2 == expected_result
وفي حالتنا ، سنشير إلى ذلك مقدمًا:
تصفية
من هنا يتبع أيضًا تخصيص وظائف الاختبار التي تتطلب بيانات من ملف ، وتلك التي تستخدم بيانات ثابتة / ديناميكية. سنطبق هذا التصفية قبل تحليل المعلومات من الملف.
يمكن أن تكون المرشحات نفسها ، على سبيل المثال:
خلاف ذلك ، يمكن تنفيذ نفس المرشح مثل هذا:
- الوسيطة الدالة
test_input
:
هذا الخيار يناسبني أكثر.
يؤدي
نحتاج إلى إضافة الجزء الذي نحلل فيه البيانات من الملف فقط. لن يكون هذا صعبًا في حالة yaml (وكذلك json ، xml ، وما إلى ذلك) ، لذلك نجمع كل شيء في الكومة.
نكتب سيناريو اختبار مثل هذا:
ملف البيانات:
# test_multiplication.yaml - !!python/tuple [1,2] - !!python/tuple [1,3] - !!python/tuple [1,5] - !!python/tuple [2,4] - !!python/tuple [3,4] - !!python/tuple [5,4]
نحصل على قائمة حالات الاختبار التالية:
pytest /test_script.py --collect-only ======================== test session starts ======================== platform linux -- Python 3.7.4, pytest-5.2.1, py-1.8.0, pluggy-0.13.0 rootdir: /pytest_habr collected 6 items <Module test_script.py> <Function test_multiplication[1-2]> <Function test_multiplication[1-3]> <Function test_multiplication[1-5]> <Function test_multiplication[2-4]> <Function test_multiplication[3-4]> <Function test_multiplication[5-4]> ======================== no tests ran in 0.04s ========================
وعن طريق تشغيل البرنامج النصي ، هذه النتيجة: 4 failed, 2 passed, 1 warnings in 0.11s
إضافات. المهام
قد ينهي هذا المقالة ، ولكن من أجل التعقيد ، سأضيف معرفات أكثر ملاءمة إلى وظيفتنا ، مع تحليل بيانات آخر ووضع علامة على كل حالة اختبار فردية.
لذلك ، على الفور ، الكود:
وفقًا لذلك ، نقوم بتغيير شكل ملف YAML الخاص بنا:
# test_multiplication.yaml - test_data: [1, 2] id: 'one_two' - test_data: [1,3] marks: ['xfail'] - test_data: [1,5] marks: ['skip'] - test_data: [2,4] id: "it's good" marks: ['xfail'] - test_data: [3,4] marks: ['negative'] - test_data: [5,4] marks: ['more_than']
ثم سيتغير الوصف إلى:
<Module test_script.py> <Function test_multiplication[one_two]> <Function test_multiplication[1_3]> <Function test_multiplication[1_5]> <Function test_multiplication[it's good]> <Function test_multiplication[3_4]> <Function test_multiplication[5_4]>
وسيتم الإطلاق: 2 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 2 warnings in 0.12s
PS: تحذيرات - لأن لا يتم تسجيل علامات مكتوبة ذاتيا في pytest.ini
في تطوير الموضوع
جاهز للمناقشة في التعليقات أسئلة حول النوع:
- ما هي أفضل طريقة لكتابة ملف yaml؟
- في أي تنسيق هو أكثر ملاءمة لتخزين بيانات الاختبار؟
- ما هي حالة الاختبار الإضافية المطلوبة في مرحلة التوليد؟
- هل أحتاج إلى معرفات لكل حالة؟