اختبار الوحدات في بايثون باستخدام pytest: دليل عملي

اختبار الوحدات في بايثون باستخدام pytest: دليل عملي

الاختبار الآلي يعد أحد أعمدة تطوير البرمجيات الحديثة، وتعد اختبارات الوحدات (Unit Testing) وسيلة فعّالة لجعل الكود أكثر قوة ومرونة وسهل الصيانة. في عالم بايثون، يبرز إطار العمل pytest كأحد أشهر وأقوى الأدوات لكتابة اختبارات وحدات بإنتاجية وسلاسة عالية. في هذا الدليل العملي، سوف نتعلم معًا كيفية كتابة اختبارات فعّالة، تنظيم مشروعك للاختبارات، استخدام fixtures، اختبار الأداء (Performance testing)، ودمج pytest مع أدوات التكامل المستمر (CI) لضمان تغطية شاملة، واستمرارية جودة البرمجية.

مقدمة حول اختبار الوحدات وأهميتها

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

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

ولمعرفة المزيد حول مبادئ كتابة الكود النظيف واحترافية البرمجة، يمكنك الاطلاع على هذا الشرح حول Clean Code في ifhmsah.

لماذا pytest؟ مميزات اختبار الوحدات في بايثون باستخدام pytest

pyttest هو إطار اختبار مرن وقوي يجمع بين البساطة والميزات المتقدمة، ويدعم كتابة اختبارات واضحة وسهلة الفهم حتى للمبتدئين، مع قدرات متقدمة ومرونة لتناسب المشاريع الكبيرة والمعقدة.

  • كتابة اختبارات أقل تفصيلاً، اعتماداً على نظام تسمية تفاعلي.
  • دعم fixtures لإعداد وسياق الاختبارات بشكل احترافي.
  • سهل التكامل مع أدوات CI و CD، وأدوات تغطية الكود.
  • يدعم اختبار البرمجيات المتزامنة وغير المتزامنة (async support).
  • مجتمع نشط ودعم لمكتبات متنوعة.

البدء: تثبيت pytest والإعداد الأولي

  1. تثبيت pytest يتم بشكل بسيط عبر pip:
    pip install pytest
  2. تأكد من وجود ملف requirements.txt ضمن مشروعك، وأضف فيه pytest لتسهيل الإدارة لاحقاً.
  3. اختبارات pytest عادة توضع داخل مجلد باسم tests/ أو test_*.py.

هيكلة مشروع اختبار وحدات مثالي

يفضل دائماً تنظيم ملفات الاختبار بصورة واضحة. مثال على هيكلة مشروع بايثون صغيرة مع اختبارات:

my_project/
  ├── src/
  │    └── calculator.py
  ├── tests/
  │    └── test_calculator.py
  ├── requirements.txt

كتابة أول اختبار وحدات باستخدام pytest

لنفترض أن لديك ملف calculator.py في مجلد src يحتوي على دالة بسيطة:

# src/calculator.py
def add(x, y):
    return x + y

لإجراء اختبار على الدالة السابقة:

# tests/test_calculator.py
from src.calculator import add

def test_add():
    assert add(3, 2) == 5

لتشغيل الاختبارات، من سطر الأوامر داخل المشروع:

pytest

سوف يقوم pytest بشكل تلقائي بالبحث عن الملفات التي تبدأ بـ test_ أو تنتهي بـ _test.py وتشغيل جميع الاختبارات التي تحمل دوال تبدأ بـ test_.

 

كتابة اختبارات فعّالة: نصائح عملية

  • سمِّ الدوال بوضوح: استخدم تسميات تعبر عن الهدف من كل اختبار (test_add_with_positive_numbers).
  • غطّ الحالات الهامة: لا تكتفي فقط بالحالات الطبيعية، بل اختبر الحالات الاستثنائية، القيم الفارغة، والقيم غير المتوقعة.
  • اجعل كل اختبار مستقل: اختبارات الوحدات الناجحة لا تعتمد على ترتيب التنفيذ، ولا تؤثر على بعضها البعض.
  • استخدم التوكيدات الصحيحة: assert ... هو قلب كل اختبار في pytest.

استخدام Fixtures في pytest لتنظيم الاختبارات وتسهيل إعادة الاستخدام

Fixtures في pytest تسمح لك بتمرير بيانات، تهيئة بيئة الاختبار، إنشاء بيانات افتراضية، أو إعداد قاعدة بيانات، بشكل تلقائي قبل (أو بعد) الاختبارات.

import pytest
from src.calculator import add

@pytest.fixture
def sample_numbers():
    return (2, 5)

def test_add_with_fixture(sample_numbers):
    x, y = sample_numbers
    assert add(x, y) == 7

وترث الاختبارات جميع مزايا fixture بدون تكرار الكود، أو تكرار خطوات الإعداد.

أنواع Fixtures الممكن استخدامها

  • Fixtures مؤقتة للإعداد والتهيئة السريعة.
  • Fixtures مع scope أوسع (scope="module" مثلاً) لتقليل الموارد create/teardown.
  • استخدام autouse=True لجعل fixture تُستخدم تلقائياً لكل اختبار داخل الملف.

اختبار الأداء باستخدام pytest وقياس الزمن

اختبار الأداء يُستخدم للتحقق من سرعة ودقة الدوال عند تنفيذها مع كميات ضخمة من البيانات. يمكنك استخدام pytest-benchmark لتسهيل عملية اختبار الأداء.

  1. تثبيت المكتبة بالإعداد:
    pip install pytest-benchmark
  2. مثال على اختبار أداء:
    def slow_add(x, y):
        # دالة اصطناعية بطيئة
        import time
        time.sleep(0.1)
        return x + y
    
    def test_add_performance(benchmark):
        result = benchmark(slow_add, 10, 5)
        assert result == 15
        
  3. تشغيل:
    pytest tests/ --benchmark-only

استخدم تقارير الأداء للتحقق أن التغييرات الجديدة لم تؤثر على سرعة الكود.

تغطية الكود البرمجي: كيف تقيس جودة اختباراتك؟

واحدة من أهم نقاط قوة pytest هي قدرته على التكامل مع أدوات تغطية الكود (code coverage)، مثل coverage.py. هذه الأدوات توفّر تقارير بنسب الكود المغطى باختبارات الوحدات، وتبرز السطور غير المختبرة.

  1. تثبيت الأداة:
    pip install pytest-cov
  2. تشغيل:
    pytest --cov=src tests/
  3. اطلع على التغطية، واعمل على إصلاح أجزاء الكود التي لم تصلها الاختبارات.

التكامل مع CI: الدمج المستمر وتغطية شاملة للكود

التكامل مع أنظمة CI (مثل GitHub Actions, GitLab CI, Travis CI) أمر بالغ الأهمية لضمان سير الإصدارات البرمجية بثقة. تستطيع إعداد السيرفر الخاص بك لفحص جميع الاختبارات تلقائياً عند كل دفع أو Merge لتنبيه المطورين بأي فشل أو نقص في التغطية.

  1. اكتب ملف الإعداد المناسب (مثل .github/workflows/python-app.yml).
  2. تأكد أن أمر التشغيل دائماً: pytest --cov=src tests/
  3. استخدم شارات تغطية تضاف تلقائياً لملفات README.

التكامل المستمر سيقلل من الأعطال المفاجئة، ويزيد موثوقية المشروع عبر جميع مراحل التطوير.

أمثلة متقدمة: اختبار الدوال غير المتزامنة (Async Functions) في pytest

إذا كنت تستخدم البرمجة غير المتزامنة (async/await) في مشروعك، فلاحظ أن pytest يدعم اختبار الدوال اللا تزامنية عبر إضافة مكتبة pytest-asyncio. للاطلاع أكثر على البرمجة غير المتزامنة وأهميتها راجع موضوعنا: البرمجة غير المتزامنة في بايثون: تحسين الأداء باستخدام async و await.

import pytest
import asyncio

async def fetch_data():
    await asyncio.sleep(0.1)
    return 42

@pytest.mark.asyncio
async def test_fetch_data():
    result = await fetch_data()
    assert result == 42

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

نصائح وخلاصة في اختبار الوحدات باستخدام pytest

  • وزع الاختبارات بشكل دوري مع كل تعديل برمجي.
  • اعتمد على fixtures لتقليل الكود المكرر، وتنظيم حالات الاختبار.
  • غطِّ الحالات الحديّة، الاستثناءات، ورسائل الخطأ.
  • ادمج التقارير مع أدوات CI للحصول على إنذارات مبكرة حول كسر الكود أو تراجع الجودة.
  • اختبر الأداء إذا كانت هناك عمليات حسابية مكثفة أو تفاعلات مع الشبكة وقواعد البيانات.
  • كن حريصاً على تحديث الاختبارات عند كل تغيير جوهري في الكود.

أسئلة شائعة حول اختبار الوحدات في بايثون باستخدام pytest

  1. هل يجب اختبار كل دالة داخل المشروع؟
    لا، يُنصح باختبار الدوال التي لديها منطق أعمال أو تؤثر على بيانات رئيسية. الدوال المساعدة والبسيطة قد لا تحتاج لاختبارات إذا كانت نتائجها واضحة للغاية.
  2. هل يمكن استبدال اختبارات الوحدات باختبار النظام (System Testing)؟
    لا، اختبارات الوحدات تكشف عن الأخطاء البرمجية الدقيقة، بينما اختبارات النظام/التكامل تركز على تدفق العمل ككل.
  3. هل يدعم pytest اختبار مشاريع بايثون الكبيرة والمتعددة الحزم؟
    نعم، مع بعض التعديلات البسيطة في ملف الإعداد وطرق الاستيراد (Imports).
  4. كيف أتعامل مع الاختبارات المعتمدة على الوقت أو الشبكة؟
    يمكنك استخدام Fixtures متخصصة، أو مكتبات محاكاة (mocks/stubs) لعزل شبكة التشغيل عن البيئة الحقيقية.

خاتمة

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

تابع قسم البرمجة في افهم صح وراجع أفضل مقالاتنا حول Clean Code وغيرها من المواضيع المتقدمة لصقل مهاراتك البرمجية.

حول المحتوى:

كيفية كتابة اختبارات فعّالة، تنظيم مشاريع الاختبار، استخدام fixtures، اختبار الأداء، ودمج pytest مع CI للحصول على تغطية عالية وجودة كود أفضل.

هل كان هذا مفيدًا لك؟

أضف تعليقك