شرح pytest: من أول اختبار إلى fixture وparametrize

بقلم فريق تقني ·· أدوات المطورين
شرح pytest: من أول اختبار إلى fixture وparametrize

اختبار واحد من 3 أسطر يكفي لتبدأ مع pytest: دالة تبدأ بـ test وجملة assert عادية، ثم أمر pytest في الطرفية. لا أصناف تُورَّث ولا أسماء دوال تحفظها. وفق استبيان مطوري بايثون الرسمي من PSF وJetBrains لعام 2024، يستخدم 53% من المطورين pytest مقابل 23% لـ unittest، بينما 36% لا يستخدمون أي إطار اختبار أصلاً — وإن كنت منهم فهذا الدليل لك.

التثبيت وأول اختبار

أحدث إصدار هو pytest 9.1.1 الصادر في 19 يونيو 2026، ويتطلب Python 3.10 فأحدث. التثبيت بالأمر الرسمي:

pip install -U pytest

وإن كنت تدير مشروعك بأداة uv كما شرحنا في دليل الانتقال من pip وvenv إلى uv، فالأمر uv add --dev pytest يضيفه إلى التبعيات التطويرية، وuv run pytest يشغّله.

يكتشف pytest اختباراتك وحده: ملفات باسم test_*.py أو *_test.py، دوال تبدأ بـ test، وأصناف تبدأ بـ Test. ضعها في مجلد tests/ منفصل. أول اختبار من الوثائق الرسمية:

def func(x):
    return x + 1

def test_answer():
    assert func(3) == 5

عند الفشل لا تصلك رسالة مبهمة، بل القيم الوسيطة نفسها: assert 4 == 5 مع بيان أن 4 هي ناتج func(3). هذا هو الفرق الجوهري عن unittest: جملة assert واحدة تغنيك عن حفظ assertEqual وأخواتها.

fixture وparametrize: تخلّص من التكرار في اختباراتك

الـfixture دالة مزيّنة بـ @pytest.fixture تجهّز ما يحتاجه الاختبار، والاختبار يطلبها بكتابة اسمها كمعامل:

import pytest

@pytest.fixture
def numbers():
    return [1, 2, 3]

def test_sum(numbers):
    assert sum(numbers) == 6

أما parametrize فيشغّل الاختبار نفسه على عدة مدخلات دفعة واحدة:

@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

تعمل الدالة 3 مرات، والتقرير يحدد بدقة أي توليفة فشلت.

أعلام تختصر دورتك اليومية

العلمماذا يفعل
-xيوقف التشغيل بعد أول فشل
-k "تعبير"يشغّل الاختبارات المطابقة بالاسم
--lfيعيد الفاشلة فقط من آخر جلسة
-vتفصيل أكثر في الإخراج
-qإخراج مختصر

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

4 أخطاء تُوقِع المبتدئين

  1. وراثة unittest.TestCase ثم محاولة استخدام fixture أو parametrize — لا يعملان داخل TestCase. اكتب دوال مستقلة خارج أي صنف.
  2. ملفا اختبار بالاسم نفسه في مجلدين بلا __init__.py يسبّبان خطأ في الاكتشاف. وحّد الأسماء أو أضف الملف.
  3. خطأ ModuleNotFoundError عند استيراد حزمتك. الحل: تخطيط src مع تثبيت قابل للتحرير، أو التشغيل بـ python -m pytest لأنه يضيف المجلد الحالي إلى المسار.
  4. دالة اختبار واحدة تفحص عدة سلوكيات، فيخفي الفشلُ الأول ما بعده. الوثائق توصي بسلوك واحد لكل اختبار، ببنية ثابتة: تجهيز، فعل، توكيد، تنظيف.

هل تبدأ بـ unittest أم pytest مباشرة؟

ابدأ بـ pytest بلا تردد. فهو يشغّل اختبارات unittest القديمة أصلاً، فلن تخسر أي كود موروث، وجملة assert أبسط من حفظ دوال المقارنة، وهو خيار 53% من المطورين. أبقِ unittest لقراءة المشاريع القديمة فقط.

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

اقرأ أيضاً