الفصل : 11 والأخير
الجزء : 1
العنوان : اختبار الكود الخاص بك
عند كتابة دالة (function) أو فئة (class)، يمكنك أيضًا كتابة اختبارات لهذا الكود. تثبت الاختبارات أن الكود يعمل كما هو مفترض استجابة لجميع أنواع المدخلات التي تم تصميمه لاستقبالها. عندما تكتب اختبارات، يمكنك أن تكون واثقًا من أن الكود سيعمل بشكل صحيح مع زيادة عدد مستخدمي برامجك. كما ستتمكن من اختبار الكود الجديد عند إضافته، للتأكد من أن التغييرات لا تؤثر على سلوك البرنامج الحالي. كل مبرمج يرتكب أخطاء، لذلك يجب على كل مبرمج اختبار كوده بانتظام لالتقاط المشاكل قبل أن يواجهها المستخدمون.
في هذا الفصل، ستتعلم كيفية اختبار الكود باستخدام pytest. مكتبة pytest هي مجموعة من الأدوات التي ستساعدك على كتابة اختباراتك الأولى بسرعة وببساطة، مع دعم اختباراتك مع زيادة تعقيدها بالتوازي مع مشروعاتك. لا تتضمن بايثون pytest بشكل افتراضي، لذا ستتعلم كيفية تثبيت المكتبات الخارجية. ستجعل معرفة كيفية تثبيت المكتبات الخارجية مجموعة واسعة من الأكواد المصممة جيدًا متاحة لك. ستوسع هذه المكتبات أنواع المشروعات التي يمكنك العمل عليها بشكل كبير.
ستتعلم كيفية بناء سلسلة من الاختبارات والتأكد من أن كل مجموعة من المدخلات تنتج الناتج المطلوب. سترى كيف يبدو الاختبار الناجح وكيف يبدو الاختبار الفاشل، وستتعلم كيف يمكن للاختبار الفاشل مساعدتك في تحسين الكود الخاص بك. ستتعلم اختبار الدوال والفئات، وستبدأ في فهم عدد الاختبارات التي يجب كتابتها لمشروع ما.
تثبيت pytest باستخدام pip
بينما تتضمن بايثون الكثير من الوظائف في المكتبة القياسية، يعتمد مطورو بايثون أيضًا بشكل كبير على الحزم الخارجية. الحزمة الخارجية هي مكتبة يتم تطويرها خارج لغة بايثون الأساسية. بعض المكتبات الخارجية الشهيرة يتم تبنيها في نهاية المطاف في المكتبة القياسية، وينتهي بها المطاف بأن تكون مضمنة في معظم تثبيتات بايثون من تلك النقطة فصاعدًا. يحدث هذا غالبًا مع المكتبات التي من غير المحتمل أن تتغير كثيرًا بمجرد إصلاح الأخطاء الأولية. يمكن لهذه الأنواع من المكتبات أن تتطور بنفس سرعة تطور اللغة العامة.
ومع ذلك، يتم الاحتفاظ بالعديد من الحزم خارج المكتبة القياسية حتى يمكن تطويرها بجدول زمني مستقل عن اللغة نفسها. تميل هذه الحزم إلى التحديث بشكل أكثر تكرارًا مما لو كانت مرتبطة بجدول تطوير بايثون. ينطبق هذا على pytest ومعظم المكتبات التي سنستخدمها في النصف الثاني من هذا الكتاب والمنهج . يجب ألا تثق بشكل أعمى في كل حزمة خارجية، ولكن أيضًا يجب ألا تنزعج حقيقة أن الكثير من الوظائف الهامة يتم تنفيذها من خلال هذه الحزم.
تحديث pip
تتضمن بايثون أداة تسمى pip تُستخدم لتثبيت الحزم الخارجية. نظرًا لأن pip يساعد في تثبيت الحزم من الموارد الخارجية، يتم تحديثه بشكل متكرر لمعالجة المشاكل الأمنية المحتملة. لذا، سنبدأ بتحديث pip. افتح نافذة طرفية جديدة وأصدر الأمر التالي:
الجزء الأول من هذا الأمر، `python -m pip`، يخبر بايثون بتشغيل الوحدة pip. الجزء الثاني، `install --upgrade`، يخبر pip بتحديث حزمة تم تثبيتها بالفعل. الجزء الأخير، `pip`، يحدد الحزمة الخارجية التي يجب تحديثها. يُظهر الإخراج أن الإصدار الحالي من pip، الإصدار 22.0.4، تم استبداله بأحدث إصدار في وقت كتابة هذا النص، 22.1.2.
يمكنك استخدام هذا الأمر لتحديث أي حزمة خارجية مثبتة على نظامك:
تثبيت pytest
الآن بعد أن تم تحديث pip، يمكننا تثبيت pytest:
لا نزال نستخدم الأمر الأساسي `pip install`، بدون علامة `--upgrade` هذه المرة. بدلاً من ذلك، نستخدم علامة `--user`، والتي تخبر بايثون بتثبيت هذه الحزمة للمستخدم الحالي فقط. يُظهر الإخراج أن أحدث إصدار من pytest تم* تثبيته بنجاح، مع عدد من الحزم الأخرى التي يعتمد عليها pytest.
يمكنك استخدام هذا الأمر لتثبيت العديد من الحزم الخارجية:
ملاحظة
إذا واجهت أي صعوبة في تشغيل هذا الأمر، حاول تشغيل نفس الأمر بدون علامة `--user`.
اختبار دالة (Testing a Function)
لتعلم الاختبار، نحتاج إلى كود لاختباره. إليك دالة بسيطة تأخذ الاسم الأول واسم العائلة، وتعيد اسمًا كاملاً منسقًا بشكل أنيق:
تجمع الدالة `get_formatted_name()` الاسم الأول واسم العائلة مع مسافة بينهما لإكمال الاسم الكامل، ثم تقوم بتكبير الحروف الأولى من الاسم وتعيده. للتحقق من أن `get_formatted_name()` يعمل، لنقم بكتابة برنامج يستخدم هذه الدالة. يتيح البرنامج `names.py` للمستخدمين إدخال الاسم الأول واسم العائلة، ومشاهدة الاسم الكامل المنسق بشكل أنيق:
يستورد هذا البرنامج `get_formatted_name()` من `name_function.py`. يمكن للمستخدم إدخال سلسلة من الأسماء الأولى وأسماء العائلة ورؤية الأسماء الكاملة المنسقة التي يتم إنشاؤها:
يمكننا أن نرى أن الأسماء المولدة هنا صحيحة. لكن افترض أننا نريد تعديل `get_formatted_name()` بحيث يمكنها أيضًا التعامل مع الأسماء الوسطى. أثناء القيام بذلك، نريد التأكد من أننا لا نكسر الطريقة التي تتعامل بها الدالة مع الأسماء التي تحتوي على الاسم الأول واسم العائلة فقط. يمكننا اختبار الكود عن طريق تشغيل `names.py` وإدخال اسم مثل Janis Joplin في كل مرة نقوم فيها بتعديل `get_formatted_name()`, لكن هذا سيصبح مرهقًا.
لحسن الحظ، يوفر pytest طريقة فعالة لأتمتة اختبار ناتج الدالة. إذا قمنا بأتمتة اختبار `get_formatted_name()`, يمكننا دائمًا أن نكون واثقين من أن الدالة ستعمل عند إعطائها الأنواع من الأسماء التي كتبنا اختبارات لها.
اختبارات الوحدة وحالات الاختبار
هناك مجموعة واسعة من النهج لاختبار البرمجيات. واحدة من أبسط أنواع الاختبار هي اختبار الوحدة (unit) . اختبار الوحدة يتحقق من أن جانبًا محددًا من سلوك وظيفة ما صحيح. حالة الاختبار هي مجموعة من اختبارات الوحدة التي تثبت معًا أن وظيفة ما تتصرف كما هو متوقع منها، ضمن النطاق الكامل من المواقف التي تتوقعها.
حالة الاختبار الجيدة تأخذ في الاعتبار جميع أنواع المدخلات الممكنة التي قد تتلقاها الوظيفة وتضم اختبارات تمثل كل من هذه المواقف. حالة الاختبار التي تغطي النطاق الكامل تتضمن مجموعة كاملة من اختبارات الوحدة التي تغطي جميع الطرق الممكنة التي يمكنك استخدامها فيها. تحقيق التغطية الكاملة في مشروع كبير يمكن أن يكون مرهقًا. في الغالب يكفي كتابة اختبارات للسلوكيات الحرجة في الكود الخاص بك ثم السعي للتغطية الكاملة فقط إذا بدأ المشروع في الاستخدام الواسع.
اختبار ناجح
مع pytest، كتابة اختبار الوحدة الأول بسيط للغاية. سنكتب دالة اختبار واحدة. دالة الاختبار ستستدعي الدالة التي نختبرها، وسنجري تأكيدًا حول القيمة التي يتم إرجاعها. إذا كان تأكيدنا صحيحًا، فسيجتاز الاختبار؛ إذا كان التأكيد غير صحيح، سيفشل الاختبار.
إليك أول اختبار لدالة get_formatted_name():
قبل تشغيل الاختبار، لنلقي نظرة أقرب على هذه الدالة. اسم ملف الاختبار مهم؛ يجب أن يبدأ بـ test_. عندما نطلب من pytest تشغيل الاختبارات التي كتبناها، سيبحث عن أي ملف يبدأ بـ test_، وسيشغل جميع الاختبارات التي يجدها في ذلك الملف.
في ملف الاختبار، نستورد أولاً الدالة التي نريد اختبارها: get_formatted_name(). ثم نحدد دالة اختبار: في هذه الحالة، test_first_last_name() ❶. هذا اسم دالة أطول مما كنا نستخدمه، وذلك لسبب جيد. أولاً، يجب أن تبدأ دوال الاختبار بالكلمة test، متبوعة بشرطة سفلية. أي دالة تبدأ بـ test_ سيتم اكتشافها من قبل pytest، وسيتم تشغيلها كجزء من عملية الاختبار. أيضًا، يجب أن تكون أسماء دوال الاختبار أطول وأكثر وصفية من اسم الدالة النموذجي. لن تقوم أبدًا باستدعاء الدالة بنفسك؛ سيجدها pytest ويشغلها من أجلك. يجب أن تكون أسماء دوال الاختبار طويلة بما يكفي لتمنحك فكرة جيدة عن السلوك الذي يتم اختباره إذا رأيت اسم الدالة في تقرير الاختبار.
بعد ذلك، نستدعي الدالة التي نختبرها ❷. هنا نستدعي get_formatted_name() بالمدخلات 'janis' و 'joplin'، تمامًا كما فعلنا عندما شغلنا names.py. نخصص القيمة المرجعة لهذه الدالة إلى formatted_name.
أخيرًا، نجري تأكيدًا ❸. التأكيد هو ادعاء حول حالة ما. هنا ندعي أن قيمة formatted_name يجب أن تكون 'Janis Joplin'.
تشغيل اختبار
إذا شغلت ملف test_name_function.py مباشرة، فلن تحصل على أي مخرجات لأننا لم نستدعِ دالة الاختبار أبدًا. بدلاً من ذلك، سنطلب من pytest تشغيل ملف الاختبار من أجلنا.
للقيام بذلك، افتح نافذة طرفية وانتقل إلى المجلد الذي يحتوي على ملف الاختبار. إذا كنت تستخدم VS Code، يمكنك فتح المجلد الذي يحتوي على ملف الاختبار واستخدام الطرفية المدمجة في نافذة المحرر. في نافذة الطرفية، أدخل الأمر pytest. إليك ما يجب أن تراه:
لنحاول فهم هذه المخرجات. أولاً وقبل كل شيء، نرى بعض المعلومات حول النظام الذي يتم تشغيل الاختبار عليه ❶. أنا أختبر هذا على نظام macOS، لذلك قد ترى بعض المخرجات المختلفة هنا. الأكثر أهمية، يمكننا رؤية أي إصدارات من Python وpytest والحزم الأخرى يتم استخدامها لتشغيل الاختبار.
بعد ذلك، نرى الدليل الذي يتم تشغيل الاختبار منه ❷: في حالتي، python_work/chapter_11. يمكننا رؤية أن pytest وجد اختبارًا واحدًا للتشغيل ❸، ويمكننا رؤية ملف الاختبار الذي يتم تشغيله ❹. النقطة الواحدة بعد اسم الملف تخبرنا بأن اختبارًا واحدًا قد اجتاز، والنسبة المئوية 100% تجعل من الواضح أن جميع الاختبارات قد تم تشغيلها. يمكن أن يحتوي المشروع الكبير على مئات أو آلاف الاختبارات، والنقاط ومؤشر النسبة المئوية يمكن أن تكون مفيدة في مراقبة التقدم العام لعملية الاختبار.
السطر الأخير يخبرنا بأن اختبارًا واحدًا اجتاز، واستغرق أقل من 0.01 ثانية لتشغيل الاختبار.
تشير هذه المخرجات إلى أن دالة get_formatted_name() ستعمل دائمًا لأسماء تتكون من الاسم الأول واسم العائلة، إلا إذا قمنا بتعديل الدالة. عندما نقوم بتعديل get_formatted_name()، يمكننا تشغيل هذا الاختبار مرة أخرى. إذا اجتاز الاختبار، نعرف أن الدالة ستظل تعمل لأسماء مثل Janis Joplin.
اختبار فاشل
كيف يبدو الاختبار الفاشل؟ لنقم بتعديل get_formatted_name() بحيث يمكنها التعامل مع الأسماء الوسطى، ولكن لنفعل ذلك بطريقة تعطل الدالة للأسماء التي تحتوي فقط على الاسم الأول واسم العائلة، مثل Janis Joplin.
إليك إصدار جديد من get_formatted_name() الذي يتطلب وسيط الاسم الأوسط:
يجب أن يعمل هذا الإصدار للأشخاص الذين لديهم أسماء وسطى، ولكن عندما نختبره، نرى أننا قمنا بتعطيل الدالة للأشخاص الذين لديهم فقط الاسم الأول واسم العائلة.
هذه المرة، تشغيل pytest يعطي المخرجات التالية:
هناك الكثير من المعلومات هنا لأن هناك الكثير مما قد تحتاج لمعرفته عندما يفشل الاختبار. أول عنصر ملحوظ في المخرجات هو F واحدة ❶، والتي تخبرنا بأن اختبارًا واحدًا قد فشل. ثم نرى قسمًا يركز على FAILURES ❷، لأن الاختبارات الفاشلة عادة ما تكون الأكثر أهمية للتركيز عليها في عملية الاختبار. بعد ذلك، نرى أن test_first_last_name() كانت دالة الاختبار التي فشلت ❸. يشير القوس ❹ إلى سطر الكود الذي تسبب في فشل الاختبار. يشير الحرف E في السطر التالي ❺ إلى الخطأ الفعلي الذي تسبب في الفشل: TypeError بسبب عدم وجود وسيط ضروري، وهو last. المعلومات الأكثر أهمية تتكرر في ملخص أقصر في النهاية، لذلك عندما تقوم بتشغيل العديد من الاختبارات، يمكنك الحصول على فكرة سريعة عن الاختبارات التي فشلت ولماذا.
الاستجابة لاختبار فاشل
ماذا تفعل عندما يفشل الاختبار؟ بافتراض أنك تتحقق من الشروط الصحيحة، يعني الاختبار الناجح أن الدالة تتصرف بشكل صحيح والاختبار الفاشل يعني أن هناك خطأ في الكود الجديد الذي كتبته. لذا عندما يفشل الاختبار، لا تغير الاختبار. إذا فعلت ذلك، قد تجتاز اختباراتك، ولكن أي كود يستدعي دالتك كما يفعل الاختبار سيتوقف فجأة عن العمل.
بدلاً من ذلك، أصلح الكود الذي يسبب فشل الاختبار. افحص التغييرات التي أجريتها على الدالة، واكتشف كيف تسببت هذه التغييرات في تعطيل السلوك المطلوب.
في هذه الحالة، كانت دالة get_formatted_name() تتطلب وسيطين فقط: الاسم الأول واسم العائلة. الآن تتطلب الاسم الأول، الاسم الأوسط، واسم العائلة. إضافة ذلك الوسيط الإلزامي الأوسط عطلت السلوك الأصلي لـ get_formatted_name(). الخيار الأفضل هنا هو جعل الاسم الأوسط اختياريًا. بمجرد القيام بذلك، يجب أن يجتاز اختبارنا للأسماء مثل Janis Joplin مرة أخرى، ويجب أن نكون قادرين على قبول الأسماء الوسطى أيضًا. لنقم بتعديل get_formatted_name() بحيث تصبح الأسماء الوسطى اختيارية ثم نشغل حالة الاختبار مرة أخرى. إذا اجتازت، سننتقل للتأكد من أن الدالة تتعامل مع الأسماء الوسطى بشكل صحيح.
لجعل الأسماء الوسطى اختيارية، ننقل الوسيط middle إلى نهاية قائمة الوسيطات في تعريف الدالة ونعطيه قيمة افتراضية فارغة. نضيف أيضًا اختبار if الذي يبني الاسم الكامل بشكل صحيح، اعتمادًا على ما إذا كان قد تم تقديم اسم أوسط أم لا:
في هذا الإصدار الجديد من get_formatted_name()، الاسم الأوسط اختياري. إذا تم تمرير اسم أوسط إلى الدالة، سيتضمن الاسم الكامل الاسم الأول، الاسم الأوسط، واسم العائلة. وإلا، سيتكون الاسم الكامل من الاسم الأول واسم العائلة فقط. الآن يجب أن تعمل الدالة لكلا النوعين من الأسماء. لاكتشاف ما إذا كانت الدالة لا تزال تعمل للأسماء مثل Janis Joplin، دعنا نشغل الاختبار مرة أخرى:
الاختبار يجتاز الآن. هذا مثالي؛ يعني أن الدالة تعمل للأسماء مثل Janis Joplin مرة أخرى، دون الحاجة لاختبار الدالة يدويًا.
إصلاح دالتنا كان أسهل لأن الاختبار الفاشل ساعدنا في تحديد كيف أن الكود الجديد عطل السلوك الحالي.
إضافة اختبارات جديدة
الآن بعد أن نعلم أن دالة get_formatted_name() تعمل مع الأسماء البسيطة مرة أخرى، دعنا نكتب اختبارًا ثانيًا للأشخاص الذين يتضمنون اسمًا أوسطًا. نقوم بذلك بإضافة دالة اختبار جديدة إلى ملف
نسمي هذه الدالة الجديدة test_first_last_middle_name(). يجب أن يبدأ اسم الدالة بـ test_ حتى تعمل الدالة تلقائيًا عند تشغيل pytest. نسمي الدالة لتوضيح السلوك الذي نختبره في get_formatted_name(). وبالتالي، إذا فشل الاختبار، سنعرف على الفور أي أنواع من الأسماء تتأثر بها.
لاختبار الدالة، نقوم باستدعاء get_formatted_name() بأسماء أولية وأخيرة ووسطى ❶، ثم نقوم بعمل تأكيد ❷ أن الاسم الكامل الذي يتم إرجاعه يتطابق مع الاسم الكامل (أولي، وسطى، وأخير) الذي نتوقعه. عند تشغيل pytest مرة أخرى، يمر الاختباران:
النقطتان ❶ تشير إلى اجتياز اختبارين، مما هو واضح أيضًا من السطر الأخير في الإخراج. هذا رائع! الآن نحن نعلم أن الدالة لا تزال تعمل لأسماء مثل Janis Joplin، ويمكننا أن نكون واثقين من أنها ستعمل أيضًا لأسماء مثل Wolfgang Amadeus Mozart.
جربها بنفسك
تمرين11-1. المدينة، البلد: اكتب دالة تقبل مدينة واسم بلد كمعلمات. يجب أن تعيد الدالة سلسلة واحدة من النوع City, Country، مثل Santiago, Chile. قم بتخزين الدالة في وحدة تسمى city_functions.py، واحفظ هذا الملف في مجلد جديد حتى لا يحاول pytest تشغيل الاختبارات التي كتبناها بالفعل.
أنشئ ملفًا يسمى test_cities.py لاختبار الدالة التي كتبتها للتو. اكتب دالة تسمى test_city_country() للتحقق من أن استدعاء الدالة بقيم مثل 'santiago' و 'chile' يؤدي إلى السلسلة الصحيحة. قم بتشغيل الاختبار وتأكد من أن test_city_country() يمر.
تمرين11-2. السكان: قم بتعديل دالتك حتى تتطلب معلمة ثالثة، وهي السكان. الآن يجب أن تعيد الدالة سلسلة واحدة من النوع City, Country - population xxx، مثل Santiago, Chile - population 5000000. قم بتشغيل الاختبار مرة أخرى وتأكد من أن test_city_country() سيفشل هذه المرة.
قم بتعديل الدالة بحيث تكون معلمة السكان اختيارية. قم بتشغيل الاختبار مرة أخرى وتأكد من أن test_city_country() يمر مرة أخرى.
اكتب اختبارًا ثانيًا يُسمى test_city_country_population() للتحقق من أنه يمكنك استدعاء الدالة بالقيم 'santiago'، 'chile'، و 'population=5000000'. قم بتشغيل الاختبارات مرة أخرى وتأكد من أن هذا الاختبار الجديد يمر.
النهاية
نكون هنا انتهينا من الجزء 1 من الفصل 11 من منهج تعلم Python من كتاب python crash course بالعربية
واذا واجهتك اي مشكلة في الفهم او ما شابه , يمكنك على الفور الذهاب الى المجتمع الخاص بنا في Telegram للمناقشة والتواصل معنا من هنا
او اذا واجهتك مشكلة في الموقع او تريد اجابة سريعة يمكنك الذهاب الى اخر صفحة في الموقع ستجد صفحة اتصل بنا موجودة يمكنك ارسالة لنا مشكلتك , وسيتم الرد عليها بسرعة جدا ان شاء الله
ويمكنك الأنضمام الى المجتمع Hidden Lock بالكامل مع جميع قنواته للأستفادة في اخر الأخبار في عالم التقنية وايضا الكتب بالمجان والكورسات والمقالات من خلال الرابط التالي لمجموعة القنوات من هنا
يمكنك ايضا متابعتنا في منصات X او Twitter سابقا , لمشاهدة الاخبار والمقالات السريعة والمهمة من
وفقط كان معكم sparrow مقدم هذه الشهادة من فريق Hidden Lock