كتاب "طريق بيثون. الحزام الأسود للتطوير والتحجيم والاختبار والنشر

صورة مرحبا ، habrozhiteli! يتيح لك مسار بايثون صقل مهاراتك المهنية والتعلم قدر الإمكان عن قدرات لغة البرمجة الأكثر شعبية. ستتعلم كيفية كتابة التعليمات البرمجية الفعالة وإنشاء أفضل البرامج في أقل وقت وتجنب الأخطاء الشائعة. لقد حان الوقت للتعرف على الحوسبة المتعددة والتذكير ، والحصول على مشورة الخبراء في مجال API وتصميم قاعدة البيانات ، وكذلك النظر داخل بيثون لتوسيع فهمك للغة. يجب عليك بدء مشروع ، والعمل مع الإصدارات ، وتنظيم الاختبارات التلقائية واختيار نمط برمجة لمهمة محددة. بعد ذلك ، ستستمر في دراسة إعلانات الوظائف الفعالة وتحديد هياكل البيانات والمكتبات المناسبة وإنشاء برامج وحزم خالية من المتاعب وتحسين البرامج على مستوى الكود.

مقتطفات. اختبارات التشغيل بالتوازي


تشغيل أجنحة الاختبار يمكن أن يكون مضيعة للوقت. هذا أمر شائع في المشاريع الكبيرة عندما يستغرق اختبار مجموعة دقائق لإكمال. بشكل افتراضي ، يجري pytest الاختبارات بالتتابع ، بترتيب معين.

نظرًا لأن معظم أجهزة الكمبيوتر تحتوي على معالجات متعددة النواة ، يمكنك الإسراع في حالة فصل الاختبارات للتشغيل على مراكز متعددة.

لهذا ، يحتوي pytest على مكون pytest-xdist يمكن تثبيته باستخدام نقطة. يمتد هذا البرنامج المساعد لسطر الأوامر pytest مع وسيطة –numprocesses (اختصار –n) ، والتي تأخذ عدد النوى المستخدمة كوسيطة. سيؤدي إطلاق pytest –n 4 إلى تشغيل مجموعة الاختبار في أربع عمليات متوازية ، مع الحفاظ على توازن بين حمل النوى المتاحة.

نظرًا لحقيقة أن عدد المراكز قد يختلف ، يقبل البرنامج المساعد أيضًا الكلمة الرئيسية التلقائية كقيمة. في هذه الحالة ، سيتم إرجاع عدد النوى المتاحة تلقائيًا.

إنشاء الكائنات المستخدمة في الاختبارات باستخدام التركيبات


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

في pytest المباريات أعلن وظائف بسيطة. يجب أن تُرجع دالة التثبيت العنصر المطلوب حتى يمكن استخدام هذا الكائن في اختبار مكان استخدامه.

هنا مثال على تركيبات بسيطة:

import pytest @pytest.fixture def database(): return <some database connection> def test_insert(database): database.insert(123) 

يتم استخدام تركيبات قاعدة البيانات تلقائيًا بواسطة أي اختبار له وسيطة قاعدة البيانات في قائمته. ستتلقى الدالة test_insert () نتيجة دالة قاعدة البيانات () كوسيطة أولى وستستخدم هذه النتيجة كما تراه مناسبًا. مع هذا الاستخدام للتركيبات ، لا تحتاج إلى تكرار رمز تهيئة قاعدة البيانات عدة مرات.

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

القائمة 6.5. مسح كائن تم التحقق منه


 import pytest @pytest.fixture def database(): db = <some database connection> yield db db.close() def test_insert(database): database.insert(123) 
نظرًا لأننا استخدمنا الكلمة المفتاحية ذات الصلة وقمنا بإنشاء مولد من قاعدة البيانات ، يتم تنفيذ التعليمات البرمجية بعد بيان العائد فقط في نهاية الاختبار. سيقوم هذا الرمز بإغلاق اتصال قاعدة البيانات في نهاية الاختبار.

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

 import pytest @pytest.fixture(scope="module") def database(): db = <some database connection> yield db db.close() def test_insert(database): database.insert(123) 

من خلال تحديد المعلمة range = "module" ، قمت بتهيئة المباراة مرة واحدة للوحدة بأكملها ، والآن سيتوفر اتصال قاعدة بيانات مفتوحة لجميع وظائف الاختبار التي تطلبها.

يمكنك تشغيل بعض التعليمات البرمجية العامة قبل الاختبار أو بعده ، وتحديد التركيبات كما تستخدم تلقائيًا مع الكلمة الأساسية autouse ، بدلاً من تحديدها كوسيطة لكل وظيفة اختبار. يضمن دمج وظيفة pytest.fixture () باستخدام وسيطة True ، الكلمة المفتاحية autouse ، أن يتم استدعاء التركيبات في كل مرة قبل إجراء الاختبار في الوحدة النمطية أو الفصل الدراسي الذي تم إعلانه فيه.

 import os import pytest @pytest.fixture(autouse=True) def change_user_env(): curuser = os.environ.get("USER") os.environ["USER"] = "foobar" yield os.environ["USER"] = curuser def test_user(): assert os.getenv("USER") == "foobar"</source     .  ,    :     ,      ,       . <h3>  </h3>           ,    ,   ,         .          Gnocchi,    . Gnocchi      <i>storage API</i>.    Python          .       ,      API   .        ,      (    storage API),  ,       .   ,   <i> </i>,     ,        .  6.6          ,    :    mysql,   —  postgresql. <blockquote><h4> 6.6.      </h4> <source lang="python">import pytest import myapp @pytest.fixture(params=["mysql", "postgresql"]) def database(request): d = myapp.driver(request.param) d.start() yield d d.stop() def test_insert(database): database.insert("somedata") 
يستقبل تثبيت برنامج التشغيل قيمتين مختلفتين كمعلمة - أسماء برامج تشغيل قاعدة البيانات التي يدعمها التطبيق. يتم تشغيل test_insert مرتين: مرة واحدة لقاعدة بيانات MySQL ، والثانية لقاعدة بيانات PostgreSQL. هذا يجعل من السهل استعادة الاختبار نفسه ، ولكن باستخدام سيناريوهات مختلفة ، دون إضافة سطور جديدة من التعليمات البرمجية.

الاختبارات المدارة مع كائنات وهمية


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

حالة استخدام واحدة لإنشاء عميل HTTP. يكاد يكون من المستحيل (أو بالأحرى ، صعب بشكل لا يصدق) إنشاء خادم HTTP يمكنك تشغيل جميع المواقف والسيناريوهات لكل قيمة ممكنة. عملاء HTTP يصعب بشكل خاص اختبار سيناريوهات الأخطاء.

تحتوي المكتبة القياسية على أمر وهمية لإنشاء كائن وهمية. بدءًا من Python 3.3 ، تم دمج mock مع مكتبة unittest.mock. لذلك ، يمكنك استخدام مقتطف الشفرة أدناه لتوفير توافق مع الإصدارات السابقة بين Python 3.3 والإصدارات الأقدم:

 try: from unittest import mock except ImportError: import mock 

مكتبة الصور سهلة الاستخدام. يتم إنشاء أي سمة متوفرة للكائن mock.Mock بشكل حيوي في وقت التشغيل. يمكن تعيين أي سمة أي قيمة. في سرد ​​6.7 ، يتم استخدام mock لإنشاء كائن وهمية للسمة وهمية.

القائمة 6.7. الوصول إلى سمة mock.Mock


 >>> from unittest import mock >>> m = mock.Mock() >>> m.some_attribute = "hello world" >>> m.some_attribute "hello world" 
يمكنك أيضًا إنشاء طريقة لكائن قابل للتغيير بشكل حيوي ، كما هو موضح في القائمة 6.8 ، حيث يمكنك إنشاء طريقة وهمية تُرجع دائمًا 42 وتأخذ ما تريده كوسيطة.

القائمة 6.8. إنشاء طريقة للكائن وهمية mock.Mock


 >>> from unittest import mock >>> m = mock.Mock() >>> m.some_method.return_value = 42 >>> m.some_method() 42 >>> m.some_method("with", "arguments") 42 
فقط بضعة أسطر ، والكائن mock.Mock لديه الآن طريقة some_method () ، والتي تُرجع 42. يستغرق أي نوع من الوسيطة ، بينما لا يوجد التحقق من ماهية الوسيطة.

يمكن أن يكون للطرق الناتجة ديناميكيًا أيضًا آثار جانبية (مقصودة). لكي لا تكون مجرد طرق مرجعية تُرجع قيمة ، يمكن تعريفها لتنفيذ تعليمات برمجية مفيدة.

تنشئ القائمة 6.9 طريقة وهمية لها تأثير جانبي - فهي تعرض السلسلة "hello world".

القائمة 6.9. إنشاء طريقة لكائن mock.Mock مع تأثير جانبي


  >>> from unittest import mock >>> m = mock.Mock() >>> def print_hello(): ... print("hello world!") ... return 43 ... ❶ >>> m.some_method.side_effect = print_hello >>> m.some_method() hello world! 43 ❷ >>> m.some_method.call_count 1 
لقد خصصنا وظيفة بأكملها لسمة some_method. من الناحية الفنية ، يتيح لك ذلك تنفيذ سيناريو أكثر تعقيدًا في الاختبار ، نظرًا لحقيقة أنه يمكنك تضمين أي رمز ضروري للاختبار في كائن دمية. بعد ذلك ، تحتاج إلى تمرير هذا الكائن إلى الوظيفة التي تتوقعه.

السمة ❷ call_count هي طريقة سهلة للتحقق من عدد مرات استدعاء الطريقة.

تستخدم المكتبة الوهمية نمط "التحقق من الإجراء": هذا يعني أنه بعد الاختبار تحتاج إلى التأكد من أن الإجراءات التي تم استبدالها بالدمى قد تم تنفيذها بشكل صحيح. يطبق سرد 6.10 أسلوب assert () على كائنات وهمية لإجراء هذه الاختبارات.

ادراج 6.10. استدعاء أساليب التحقق


  >>> from unittest import mock >>> m = mock.Mock() ❶ >>> m.some_method('foo', 'bar') <Mock name='mock.some_method()' id='26144272'> ❷ >>> m.some_method.assert_called_once_with('foo', 'bar') >>> m.some_method.assert_called_once_with('foo', ❸mock.ANY) >>> m.some_method.assert_called_once_with('foo', 'baz') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/dist-packages/mock.py", line 846, in assert_cal led_once_with return self.assert_called_with(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/mock.py", line 835, in assert_cal led_with raise AssertionError(msg) AssertionError: Expected call: some_method('foo', 'baz') Actual call: some_method('foo', 'bar') 
لقد أنشأنا طرقًا باستخدام وسيطات foo و bar كاختبارات عن طريق استدعاء الأسلوب ❶. طريقة سهلة لاختبار المكالمات إلى كائنات وهمية هي استخدام أساليب assert_called () ، مثل assert_called_once_with () ❷. لهذه الطرق ، تحتاج إلى تمرير القيم التي تتوقع استخدامها عند استدعاء الأسلوب الوهمي. إذا كانت القيم التي تم تمريرها تختلف عن تلك المستخدمة ، فسوف يثير النموذج وهمية استثناء AssertionError. إذا كنت لا تعرف الوسائط التي يمكن تمريرها ، فاستخدم mock.ANY كقيمة ❸؛ سوف تحل محل أي حجة مرت إلى طريقة وهمية.

يمكن أيضًا استخدام المكتبة الوهمية لاستبدال دالة أو طريقة أو كائن من وحدة خارجية. في قائمة 6.11 ، استبدلنا الدالة os.unlink () بوظيفة الدمية الخاصة بنا.

ادراج 6.11. باستخدام mock.patch


 >>> from unittest import mock >>> import os >>> def fake_os_unlink(path): ... raise IOError("Testing!") ... >>> with mock.patch('os.unlink', fake_os_unlink): ... os.unlink('foobar') ... Traceback (most recent call last): File "<stdin>", line 2, in <module> File "<stdin>", line 2, in fake_os_unlink IOError: Testing! 
عند استخدامها كمدير للسياق ، يستبدل mock.patch () الوظيفة الهدف بالوظيفة التي نختارها. يعد ذلك ضروريًا بحيث تستخدم التعليمات البرمجية التي يتم تنفيذها داخل السياق الطريقة الصحيحة. باستخدام طريقة mock.patch () ، يمكنك تعديل أي جزء من الكود الخارجي ، مما يجبره على التصرف بطريقة لاختبار جميع شروط التطبيق (إدخال 6.12).

ادراج 6.12. باستخدام mock.patch () لاختبار العديد من السلوكيات


  from unittest import mock import pytest import requests class WhereIsPythonError(Exception): passdef is_python_still_a_programming_language(): try: r = requests.get("http://python.org") except IOError: pass else: if r.status_code == 200: return 'Python is a programming language' in r.content raise WhereIsPythonError("Something bad happened") def get_fake_get(status_code, content): m = mock.Mock() m.status_code = status_code m.content = content def fake_get(url): return m return fake_get def raise_get(url): raise IOError("Unable to fetch url %s" % url) ❷ @mock.patch('requests.get', get_fake_get( 200, 'Python is a programming language for sure')) def test_python_is(): assert is_python_still_a_programming_language() is True @mock.patch('requests.get', get_fake_get( 200, 'Python is no more a programming language')) def test_python_is_not(): assert is_python_still_a_programming_language() is False @mock.patch('requests.get', get_fake_get(404, 'Whatever')) def test_bad_status_code(): with pytest.raises(WhereIsPythonError): is_python_still_a_programming_language() @mock.patch('requests.get', raise_get) def test_ioerror(): with pytest.raises(WhereIsPythonError): is_python_still_a_programming_language() 


تطبق القائمة 6.12 حالة اختبار تبحث عن جميع حالات Python وهي سلسلة لغة برمجة في python.org ❶. لا يوجد خيار حيث لن يجد الاختبار أي سطر معين على صفحة الويب المحددة. للحصول على نتيجة سلبية ، تحتاج إلى تغيير الصفحة ، ولكن هذا لا يمكن القيام به. ولكن بمساعدة الوهم ، يمكنك الذهاب إلى الخدعة وتغيير سلوك الطلب بحيث يُرجع استجابة وهمية بصفحة وهمية لا تحتوي على سلسلة معينة. سيسمح لك ذلك باختبار سيناريو سلبي فيه python.org لا يحتوي على سلسلة معينة ، وتأكد من أن البرنامج يتعامل مع مثل هذه الحالة بشكل صحيح.

يستخدم هذا المثال نسخة ديكور mock.patch () . لا يتغير سلوك الكائن الوهمي ، وكان من السهل تعيين مثال في سياق وظيفة الاختبار.

سيساعد استخدام كائن وهمية في محاكاة أي مشكلة: يعرض الخادم خطأ 404 أو خطأ إدخال / إخراج أو خطأ تأخير في الشبكة. يمكننا التأكد من أن الكود يُرجع القيم الصحيحة أو يلقي الاستثناء الصحيح في كل حالة ، مما يضمن السلوك المتوقع للكود.

تحديد رمز لم تختبر مع التغطية


إضافة كبيرة إلى اختبار الوحدة هي أداة التغطية. [تغطية الشفرة هي مقياس يستخدم في الاختبار. يوضح النسبة المئوية للكود المصدري للبرنامج الذي تم تنفيذه أثناء عملية الاختبار - ed. ] ، الذي يجد قطعة غير مختبرة من الكود. يستخدم تحليل التعليمات البرمجية وأدوات التتبع لتحديد الخطوط التي تم تنفيذها. في اختبار الوحدة ، يمكن أن يكشف عن أجزاء الكود التي تم إعادة استخدامها والتي لم يتم استخدامها على الإطلاق. يُعد إنشاء الاختبارات أمرًا ضروريًا ، والقدرة على معرفة أي جزء من الشفرة نسيت تغطيتها مع الاختبارات تجعل هذه العملية أكثر متعة.

قم بتثبيت وحدة التغطية عبر النقطة لتتمكن من استخدامها من خلال قشرة قذيفة.

ملاحظة


يمكن أيضًا تسمية الأمر python-cover إذا تم تثبيت الوحدة من خلال مُثبت نظام التشغيل لديك. مثال على ذلك هو نظام التشغيل دبيان.


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

عند استخدام pytest ، قم بتثبيت البرنامج المساعد pytest-cov من خلال pip pip pytest-pycov وإضافة بعض المفاتيح لتوليد إخراج مفصل من الكود غير المختبر (سرد 6.13).

القائمة 6.13. باستخدام pytest والتغطية


 $ pytest --cov=gnocchiclient gnocchiclient/tests/unit ---------- coverage: platform darwin, python 3.6.4-final-0 ----------- Name Stmts Miss Branch BrPart Cover --------------------------- gnocchiclient/__init__.py 0 0 0 0 100% gnocchiclient/auth.py 51 23 6 0 49% gnocchiclient/benchmark.py 175 175 36 0 0% --snip-- --------------------------- TOTAL 2040 1868 424 6 8% === passed in 5.00 seconds === 
يتيح خيار --cov إخراج تقرير التغطية في نهاية الاختبار. يجب تمرير اسم الحزمة كوسيطة حتى يقوم البرنامج المساعد بتصفية التقرير بشكل صحيح. سوف يحتوي الإخراج على سطور من التعليمات البرمجية التي لم يتم تنفيذها ، مما يعني أنه لم يتم اختبارها. كل ما تبقى لك هو فتح المحرر وكتابة اختبار لهذا الرمز.

وحدة التغطية أفضل - فهي تسمح لك بإنشاء تقارير واضحة بتنسيق HTML. كل ما عليك هو إضافة -cov-report-html وصفحات HTML ستظهر في دليل htmlcov حيث يمكنك تشغيل الأمر. ستُظهر كل صفحة أجزاء الكود المصدري التي كانت تعمل أو لم تكن قيد التشغيل.

إذا كنت ترغب في المضي قدمًا إلى أبعد من ذلك ، فاستخدم –غطاء تفشل أقل من COVER_MIN_PERCENTAGE ، مما يؤدي إلى فشل مجموعة الاختبار في حالة عدم تغطية الحد الأدنى من النسبة المئوية للرمز. على الرغم من أن نسبة كبيرة من التغطية هدف جيد ، وأن أدوات الاختبار مفيدة للحصول على معلومات حول حالة تغطية الاختبار ، فإن النسبة المئوية بحد ذاتها ليست مفيدة للغاية. يوضح الشكل 6.1 مثال على تقرير التغطية يوضح النسبة المئوية للتغطية.

على سبيل المثال ، فإن تغطية الشفرة مع الاختبارات 100٪ هدف يستحق ، ولكن هذا لا يعني بالضرورة أن الشفرة قد تم اختبارها بالكامل. تُظهر هذه القيمة فقط أنه تم استيفاء جميع سطور التعليمات البرمجية في البرنامج ، ولكنها لا تشير إلى أن جميع الشروط قد تم اختبارها.

يجدر استخدام معلومات التغطية لتوسيع مجموعة الاختبار وإنشائها لرمز لا يعمل. هذا يبسط دعم المشروع ويحسن جودة الكود الكلي.

صورة


عن المؤلف


يعمل Julien Danju على اختراق البرامج المجانية منذ حوالي عشرين عامًا ، ويقوم بتطوير برامج Python منذ ما يقرب من اثني عشر عامًا. يقود حاليًا فريق التصميم لمنصة السحابة الموزعة المستندة إلى OpenStack ، والتي تمتلك أكبر قاعدة بيانات مفتوحة المصدر لـ Python ، مع حوالي مليوني ونصف سطر من التعليمات البرمجية. قبل تطوير الخدمات السحابية ، أنشأ Julien مدير النوافذ وساهم في تطوير العديد من المشاريع ، مثل Debian و GNU Emacs.

حول محرر العلوم


يعمل مايك دريسكول في البرمجة في بايثون منذ أكثر من عقد. لفترة طويلة ، كتب عن بيثون على الفأر مقابل. بايثون . مؤلف العديد من كتب Python: Python 101 ، و Python مقابلات ، و ReportLab: PDF Processingwith Python. يمكنك العثور على Mike على Twitter و GitHub:driscollis.

»يمكن الاطلاع على مزيد من المعلومات حول الكتاب على موقع الناشر
» المحتويات
» مقتطفات

خصم 25٪ على كوبون الباعة المتجولين - بيثون

عند دفع النسخة الورقية من الكتاب ، يتم إرسال كتاب إلكتروني عبر البريد الإلكتروني.

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


All Articles