تحديث الأساسيات الحديثة: خطة تقنية شاملة للتحويل

تقديم

الاختبار مهم في عملية تطوير البرمجيات ليتأكد من أن البرمجيات تتصرف كما يتوقع و أنها خالية من الأخطاء. في بيتشوني (Python), pytest هو نظام اختبار مشهور يوفر عدة مزايا عن وحدة الاختبار الاولية unit test وهو وحدة تجريد برمجيات بيتشوني وجزء من المكتبة الاساسية. pytest تشمل نموذج ترميزي أبسط و إظهار أفضل وأدوات مركزية قوية ومنظومة برمجية تمتع بالمتزايدات. سوف يقود هذا التورية بشكل سهل من خلال تكوين تطبيق Flask و تراكم معدات pytest وكتابة اختبارات وحدة باستخدام pytest.

المقادير المطلوبة

قبل بدء العمل ، سوف تحتاج التالي:

  • خوادم تشغيل لأوبونتو ومستخدم غير رئيسي يمتلك ممتلكات المسؤولية (sudo) وجراء قيود النارية. للتوجيه في كيفية إنشاء هذا ، أخذ من هذه القائمة هذه وتتبع إرشادات إعداد الخوادم الأولية منا. يجب أن تتأكد من أنك تعمل بنسخة مدعومة من Ubuntu.

  • معرفة جيدة بالخط السلطاني للLinux. يمكنك الذهاب إلى هذه الدراسة المرجعية عن الخط السلطاني للLinux.

  • فهم بسيط للبرمجيات التي تتطلب Python والتجريد التجاري في Python (pytest). يمكنك الاشتراك في دراستنا عن التجريد التجاري للبرمجيات Python (pytest) لتعرف المزيد عن pytest.

  • تم تثبيت Python 3.7 أو أعلى في نظامك الأوبونتو. يمكنك أن تشاهد درسنا الموجه عن كيفية تشغيل script برمجي Python في Ubuntu لتعلم كيفية تشغيل script برمجي Python في Ubuntu.

لماذا pytest هو خيار أفضل مقارنة بالإختبار البرمجي المتضمن unittest

pytest يوفر عدة مزايا عن الإختبار البرمجي المبني بunittest:

  • يسمح pytest لك بكتابة التجارب بأقل كود للبناء، باستخدام تعريفات assert بسيطة بدلاً من الطرق الأكثر تعبيرًا التي يتوجب استخدامها بواسطة unittest.

  • يوفر مخرجات أكثر تفصيلًا وقابلة للقراءة، مما يسهل تحديد مكان وسبب فشل الاختبار.

  • تتيح أدوات Pytest إعدادات اختبار أكثر مرونة وقابلة لإعادة الاستخدام مقارنة بأساليب setUp وtearDown الخاصة بـ unittest.

  • يسهل تشغيل نفس وظيفة الاختبار مع مجموعات متعددة من المدخلات، وهو ما ليس بنفس السهولة في unittest.

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

  • يكتشف Pytest تلقائيًا ملفات الاختبار والوظائف التي تتطابق مع اتفاقيات التسمية الخاصة به، مما يوفر الوقت والجهد في إدارة مجموعات الاختبار.

بما يحدده المزايا، يتم استخدام pytest غالبًا كخيار مفضل للاختبارات البيانية الحديثة للPython. دعونا نستعمل مجموعة pytest لإنشاء تطبيق Flask وكتابة الاختبارات الوحدية له.

الخطوة 1 – إعداد البيئة

يشترك Ubuntu 24.04 بPython 3 بشكل افتراضي. افتح محطة الأوامر وأدخل الأوامر التالية لتأكد من تثبيت Python 3:

root@ubuntu:~# python3 --version
Python 3.12.3

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

root@ubuntu:~# sudo apt install python3

من ثم، يتوجب عليك تثبيت معالج الحزم الpip على نظامك:

root@ubuntu:~# sudo apt install python3-pip

بمجرد تثبيت pip، دعونا نتمكن من تثبيت Flask.

الخطوة 2 – إنشاء تطبيق Flask

دعونا نبدأ بإنشاء تطبيق Flask بسيط. قم بإنشاء مجلد جديد لمشروعك وتحريك فيه:

root@ubuntu:~# mkdir flask_testing_app
root@ubuntu:~# cd flask_testing_app

الآن، دعونا نخلق ونتفعل بيئة افتراضية لإدارة ال依存关系:

root@ubuntu:~# python3 -m venv venv
root@ubuntu:~# source venv/bin/activate

قم بتثبيت Flask بواسطة pip:

root@ubuntu:~# pip install Flask

الآن، دعونا نخلق تطبيق Flask بسيط. قم بإنشاء ملف جديد يدعى app.py وأضف التعبير التالي:

app.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def home():
    return jsonify(message="Hello, Flask!")

@app.route('/about')
def about():
    return jsonify(message="This is the About page")

@app.route('/multiply/<int:x>/<int:y>')
def multiply(x, y):
    result = x * y
    return jsonify(result=result)

if __name__ == '__main__':
    app.run(debug=True)

يمتلك هذا التطبيق ثلاث مسارات:

  • /: يعيد معالجة “مرحبا، Flask!” رسالة بسيطة.
  • /about: يعيد معالجة “هذه صفحة المعلومات” رسالة بسيطة.
  • /multiply/<int:x>/<int:y>: يضاعف عددين ويعيد معالجة النتيجة.

لتشغيل التطبيق، قم بأجراء أمر التالي:

root@ubuntu:~# flask run
output
* Serving Flask app "app" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

من الناتج الأعلى يمكنك أن تلاحظ أن المخزن يعمل على http://127.0.0.1 ويستمع على نقطة 5000. افتح مكالمة Ubuntu أخرى وأجراء أوامر curl التالية واحدة بعد الأخرى:

  • GET: curl http://127.0.0.1:5000/
  • GET: curl http://127.0.0.1:5000/about
  • GET: curl http://127.0.0.1:5000/multiply/10/20

دعونا نفهم ما تفعله هذه ال solicitudes GET:

  1. curl http://127.0.0.1:5000/:
    هذه ترسل solicitud GET إلى مسار الجذر (‘/’) لتطبيق Flask الخاص بنا. يستجيب الخادم بجيب نظم البيانات الذي يحوي رسالة “Hello, Flask!”، مظاهرة وظيفة بسيطة لمسار وطننا.

  2. curl http://127.0.0.1:5000/about:
    هذه ترسل solicitud GET إلى مسار /about. يستجيب الخادم بجيب نظم البيانات الذي يحوي رسالة “This is the About page”. هذا يوضح أن مسارنا يعمل بشكل صحيح.

  3. curl http://127.0.0.1:5000/multiply/10/20:
    هذه ترسل solicitud GET إلى مسار /multiply مع ما يقارب عددين: 10 و 20. يضاعف الخادم هذه الأرقام و يستجيب بجيب نظم البيانات الذي يحوي النتيجة (200). هذا يوضح أن مسار التكامل يمكنه بشكل صحيح التعامل مع معاملات الURL الأولية والقيام بالحوسبة.

تلك ال solicitudes GET تسمح لنا بالتفاعل مع نقاط الAPI النهائية لتطبيقاتنا الFlask ، تستعيد المعلومات أو تحرك الأفعال على الخوادم دون تعديل أي بيانات. إنها مفيدة للحصول على البيانات ، تفقيد قابلية تشغيل نقاط النهاية وتحقق من أن تقوم مساراتنا بالإستجابة كما نتوقع.

دعونا نرى كل واحد من هذه ال solicitudes GET فعليًا:

root@ubuntu:~# curl http://127.0.0.1:5000/
Output
{"message":"Hello, Flask!"}
root@ubuntu:~# curl http://127.0.0.1:5000/about
Output
{"message":"This is the About page"}
root@ubuntu:~# curl http://127.0.0.1:5000/multiply/10/20
Output
{"result":200}

خطوة 3 – تثبيت pytest وكتابة اختبارك الأول

حالما أنه تطبيق Flask بسيط ، دعونا ن Instalar pytest ونكتب بعض اختباراتك.

قم بتثبيت pytest باستخدام pip:

root@ubuntu:~# pip install pytest

أنشئ 目录 tests لتخزين ملفات الاختبارات الخاصة بك:

root@ubuntu:~# mkdir tests

حالاً، لنقم بإنشاء ملف جديد يسمى test_app.py و نضيف التالي البرمجية:

test_app.py
# توافير موديل الأوامر الخاص بـ sys لتعديل بيئة التشغيل النقدية للبوتون
import sys
# توافير موديل الأوامر الخاص بـ os للتفاعل مع نظام التشغيل
import os

# إضافة إلى مسار sys.path المجالد الوالد
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# توافير مصادر التطبيق Flask من الملف الرئيسي للتطبيق
from app import app 
# توافير موديل pytest للكتابة وتنفيذ الاختبارات
import pytest

@pytest.fixture
def client():
    """A test client for the app."""
    with app.test_client() as client:
        yield client

def test_home(client):
    """Test the home route."""
    response = client.get('/')
    assert response.status_code == 200
    assert response.json == {"message": "Hello, Flask!"}

def test_about(client):
    """Test the about route."""
    response = client.get('/about')
    assert response.status_code == 200
    assert response.json == {"message": "This is the About page"}

def test_multiply(client):
    """Test the multiply route with valid input."""
    response = client.get('/multiply/3/4')
    assert response.status_code == 200
    assert response.json == {"result": 12}

def test_multiply_invalid_input(client):
    """Test the multiply route with invalid input."""
    response = client.get('/multiply/three/four')
    assert response.status_code == 404

def test_non_existent_route(client):
    """Test for a non-existent route."""
    response = client.get('/non-existent')
    assert response.status_code == 404

دعونا نفك الوظائف في هذا الملف الاختباري:

  1. @pytest.fixture def client():
    هذه معالجة pytest تولد متصل اختبار لتطبيقنا الFlask. إنها تستخدم طريقة app.test_client() لتوليد متصل يمكنه الإرسال إلى تطبيقنا طلبات دون تشغيل المزيد من المخزن. تلك ال声明 yield تسمح للمتصل بالاستخدام في الاختبارات ومن ثم إغلاقه بطريقة صحيحة بعد كل اختبار.

  2. الوظيفة test_home التي تقوم باختبار المسار الرئيسي (/) لتطبيقنا. إنها ترسل solicitud GET إلى المسار من خلال المتسكر التجاري، ثم تؤكد أن تم إرسال رسالة تعاونية تطابق الرسالة المتوقعة.

  3. def test_about(client):
    مماثلةً لـ test_home، يقوم هذا المتسكر باختبار مسار المعلومات العامة (/about). يتم تحقيق رمز الوصول 200 وتحقيق محتوى الردود البيانية بالمعلومات المتوقعة.
  4. def test_multiply(client):
    هذه الوظيفة تقوم باختبار مسار التكامل مع تعامل جيد (/multiply/3/4). يتم تحقيق أن تم إرسال رمز الوصول 200 وتحقيق أن تحتوي الردود البيانية على نتيجة التكامل الصحيحة.
  5. def test_multiply_invalid_input(client):
    هذه ال函数 تختبر مسار التضاعف مع معطيات غير سليمة (multiply/three/four). تتأكد من أن رمز الحالة هو 404 (غير موجود)، وهو السلوك المتوقع عندما لا يمكن للمسار تطابق معادن المعطيات النصية للمتطلبات العددية اللازمة.

  6. def test_non_existent_route(client):
    هذه ال函数 تختبر تصرف التطبيق عند مرور بالمسار الغير موجود. إنها ترسل solicitud GET إلى /non-existent، وهو غير معروف في تطبيقنا ال Flask. التجربة تؤكد أن رمز الرد هو 404 (غير موجود)، مما يتأكد من أن تطبيقنا يتم معالجة ال solicitudes بالمسارات الغير معروفة بطريقة صحيحة.

هذه التجارب تغطي الوظائف الأساسية لتطبيقنا ال Flask، تتأكد من أن كل مسار يستجيب بالطريقة الصحي

خطوة 4 – تشغيل التests

لتشغيل التests، قم بتنفيذ الأمر التالي:

root@ubuntu:~# pytest

بشكل افتراضي، سيقوم عملية اكتشاف pytest بالبحث تلقائيًا في المجلد الحالي والمجلدات الفرعية للملفات التي تبدأ بـ “test_” أو تنتهي بـ “_test”. ستتم تنفيذ التests الموجودة في تلك الملفات. يمكنك رؤية ما يشابه:

Output
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0 rootdir: /home/user/flask_testing_app collected 5 items tests/test_app.py .... [100%] ======================================================= 5 passed in 0.19s ========================================================

هذا يعني أن جميع التests تم موافقتهم بنجاح.

خطوة 5: استخدام المعاملات في pytest

المعاملات هي المكونات التي تستخدم لتوفير البيانات أو الموارد للتests. يمكن استخدامها لإنشاء وإلغاء البيئات التجارية للتests، تحميل البيانات، أو إنجاز مهام أخرى. في pytest، تتعرف المعاملات بواسطة وصفاً @pytest.fixture.

هذه هي الطريقة التي يمكنك تحسين معاملة الزائر. قم بتحديث المعاملة الزائر لتستخدم المنطقة المحلية والخارجية للتعامل:

test_app.py
@pytest.fixture
def client():
    """Set up a test client for the app with setup and teardown logic."""
    print("\nSetting up the test client")
    with app.test_client() as client:
        yield client  # هذه هي مكان حدوث الاختبار
    print("Tearing down the test client")

def test_home(client):
    """Test the home route."""
    response = client.get('/')
    assert response.status_code == 200
    assert response.json == {"message": "Hello, Flask!"}

def test_about(client):
    """Test the about route."""
    response = client.get('/about')
    assert response.status_code == 200
    assert response.json == {"message": "This is the About page"}

def test_multiply(client):
    """Test the multiply route with valid input."""
    response = client.get('/multiply/3/4')
    assert response.status_code == 200
    assert response.json == {"result": 12}

def test_multiply_invalid_input(client):
    """Test the multiply route with invalid input."""
    response = client.get('/multiply/three/four')
    assert response.status_code == 404

def test_non_existent_route(client):
    """Test for a non-existent route."""
    response = client.get('/non-existent')
    assert response.status_code == 404

يضيف هذا الإعداد أعطال لإظهار أولويات الإعداد والتنظيف في مخرج التجارب. يمكن محاولة 代替هذه الإشارات بالكود الحقيقي للإدارة الموارد إذا كان ذلك من الضروري.

دعونا نحاول تنفيذ الاختبارات مرة أخرى:

root@ubuntu:~# pytest -vs

العلامة -v تزيد من كفاءة التوصيل، والعلامة -s تسمح للأعطال بأن يظهر في مخرج المكالمات التيضية.

يجب أن ترى المخرج التالي:

Output
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0 rootdir: /home/user/flask_testing_app cachedir: .pytest_cache collected 5 items tests/test_app.py::test_home Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_about Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply_invalid_input Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_non_existent_route Setting up the test client PASSED Tearing down the test client ============================================ 5 passed in 0.35s =============================================

خطوة 6: إضافة حالة اختبار فشل

دعونا نأخذ حالة اختبار فشل في الملف الاختباري ال现成. قم بتعديل ملف test_app.py وأضف تحت المربع التالي لحالة اختبار فشل لنت بنتيجة خاطئة:

test_app.py
def test_multiply_edge_cases(client):
    """Test the multiply route with edge cases to demonstrate failing tests."""
    # اختبار بصفر
    response = client.get('/multiply/0/5')
    assert response.status_code == 200
    assert response.json == {"result": 0}

    # اختبار بأرقام كبيرة (قد يفشل هذا الإختبار إذا لم يتم تعامله بطريقة  correct)
    response = client.get('/multiply/1000000/1000000')
    assert response.status_code == 200
    assert response.json == {"result": 1000000000000}

    # اختبار تعمدي للفشل: نتيجة خاطئة
    response = client.get('/multiply/2/3')
    assert response.status_code == 200
    assert response.json == {"result": 7}, "This test should fail to demonstrate a failing case"

دعونا نفك القسم test_multiply_edge_cases وشرح ما يفعل كل جزء منه:

  1. اختبار بصفر: هذا الاختبار يتفقد إذا تم تعامل وظيفة الضرب بشكل صحيح مع الصفر. نتوقع أن نحصل على 0 عندما نضرب أي رقم بصفر. هذه حالة حافة مهمة للاختبار لأن بعض تطويرات القيام بالمشاكل مع ضرب الصفر.
  2. اختبار بأرقام كبيرة:
    هذا الاختبار يؤكد إذا كانت وظيفة الضرب قادرة على التعامل مع الأرقام الكبيرة دون تدفق أو قضايا دقة. نحن نضرب قيمتين بمليون، نتوقع نتيجة بتريليون. هذا الاختبار هام لأنه يتحقق من الحدود العليا لقدرات الوظيفة. لاحظ أنه قد يفشل إذا لم يتم التعامل مع الأرقام الكبيرة بصورة صحيحة في تنفيذ الخادم، مما قد يشير إلى الحاجة إلى مكتبات الأرقام الكبيرة أو نوع مختلف من البيانات.
  3. اختبار مخطط للفشل:
    هذا الاختبار مخطط للفشل بتعمد. يتحقق إذا كان 2 * 3 يساوي 7، وهذا خطأ. هذا الاختبار يهدف إلى إظهار كيف يبدو اختبار فاشل في مخرجات الاختبار. هذا يساعد في فهم كيفية تحديد وتصليل الاختبارات الفاشلة، وهو مهارة هامة في التطوير المقود بالاختبار وعمليات الصيانة.

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

دعونا نحاول تنفيذ الاختبارات مجددًا:

root@ubuntu:~# pytest -vs

يجب أن ترى الخريطة التالية:

Output
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0 rootdir: /home/user/flask_testing_app cachedir: .pytest_cache collected 6 items tests/test_app.py::test_home Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_about Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply_invalid_input Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_non_existent_route Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply_edge_cases Setting up the test client FAILED Tearing down the test client ================================================================= FAILURES ================================================================== _________________________________________________________ test_multiply_edge_cases __________________________________________________________ client = <FlaskClient <Flask 'app'>> def test_multiply_edge_cases(client): """Test the multiply route with edge cases to demonstrate failing tests.""" # اختبار بصفر response = client.get('/multiply/0/5') assert response.status_code == 200 assert response.json == {"result": 0} # اختبار بأرقام كبيرة (قد يفشل إذا لم يتم التعامل معها صحيحًا) response = client.get('/multiply/1000000/1000000') assert response.status_code == 200 assert response.json == {"result": 1000000000000} # اختبار مقصود بالفشل: نتيجة خاطئة response = client.get('/multiply/2/3') assert response.status_code == 200 > assert response.json == {"result": 7}, "This test should fail to demonstrate a failing case" E AssertionError: This test should fail to demonstrate a failing case E assert {'result': 6} == {'result': 7} E E Differing items: E {'result': 6} != {'result': 7} E E Full diff: E { E - 'result': 7,... E E ...Full output truncated (4 lines hidden), use '-vv' to show tests/test_app.py:61: AssertionError ========================================================== short test summary info ========================================================== FAILED tests/test_app.py::test_multiply_edge_cases - AssertionError: This test should fail to demonstrate a failing case ======================================================== 1 failed, 5 passed in 0.32s ========================================================

الرسالة الفشلية أعلاه تشير إلى أن اختبار test_multiply_edge_cases في الملف tests/test_app.py فشل. بالتحديد ، الاعتماد الأخير في هذه المادة التي أسببت الفشل.

هذا الفشل المقصودي مفيد لعرض كيفية تقديم الاختبارات الفشلية والمعلومات التي تقدم في الرسائل الفشلية. يظهر العقد التالي حي

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

خاتم

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

يمكنك الحصول على معلومات أكثر بررسي لFlask وPytest من المستندات الرسمية.

Source:
https://www.digitalocean.com/community/tutorials/unit-test-in-flask