خانه / آموزش‌ها / آموزش جامع ماژول Doctest در پایتون

آموزش جامع ماژول Doctest در پایتون

🐍 HomeOfPython
|
📅 1404/10/16

سطح مقدماتی (Beginner Level)

ماژول doctest در پایتون یکی از ابزارهای استاندارد و داخلی برای تست کد است. فلسفه اصلی این ابزار بسیار ساده و جذاب است: تست‌ها را دقیقاً مانند یک جلسه تعاملی در پایتون (REPL) داخل توضیحات کد (Docstrings) بنویسید.

این کار دو هدف را همزمان دنبال می‌کند:

  1. مستندسازی: کاربران می‌فهمند کد شما چگونه کار می‌کند.
  2. تست خودکار: پایتون بررسی می‌کند که آیا خروجی کد دقیقاً با آنچه در مستندات ادعا کرده‌اید یکسان است یا خیر.

۱. ساختار اولیه و نوشتن تست

برای نوشتن یک doctest، کافی است داخل """docstring""" تابع یا کلاس خود، خطوطی که با >>> شروع می‌شوند را اضافه کنید. خط بعدی باید خروجی مورد انتظار باشد.

مثال اول: تابع ساده ریاضی

در این مثال، یک تابع جمع ساده داریم. دقت کنید که فاصله بعد از >>> الزامی است.

Python

۲. مدیریت خطاها (Exceptions)

یکی از مفاهیم مهم در سطح مقدماتی، تست کردن کدهایی است که باید خطا (Error) برگردانند. doctest می‌تواند بررسی کند که آیا خطای مورد نظر رخ داده است یا خیر.

مثال دوم: تست تقسیم بر صفر

برای تطبیق خطا، معمولاً خط اول (Traceback...) و خط آخر (نوع و پیام خطا) مهم هستند.

Python

۳. مثالی که نیاز به زمینه خارجی دارد (Static)

گاهی اوقات کدی که در داک‌استرینگ می‌نویسید به متغیرهای گلوبال یا شرایط خاصی نیاز دارد که در یک اسکریپت ساده python-run قابل نمایش نیست (مثلاً وابستگی به فایل دیتابیس). در این حالت کد را به صورت استاتیک نمایش می‌دهیم.

python
def get_user_data(user_id):
    """
    اطلاعات کاربر را از دیتابیس می‌خواند.
    (فرض کنید دیتابیس متصل است)

    >>> user = get_user_data(1)
    >>> user['name']
    'Ali'
    """
    # این تابع فقط یک نمونه است و اجرا نمی‌شود
    pass

سطح پیشرفته (Professional Level)

در سطح حرفه‌ای، doctest چالش‌هایی دارد که اگر آن‌ها را نشناسید، تست‌های شما مدام با شکست مواجه می‌شوند (Flaky Tests). مواردی مانند ترتیب نمایش دیکشنری‌ها، فاصله‌های خالی (Whitespace)، و آدرس‌های حافظه که در هر اجرا تغییر می‌کنند.

۱. استفاده از Directive ها (Flags)

ماژول doctest پرچم‌هایی (Flags) دارد که رفتار مقایسه را تغییر می‌دهند. این پرچم‌ها با کامنت # doctest: +FLAG_NAME در جلوی خط تست اعمال می‌شوند.

الف) مدیریت خروجی‌های متغیر با ELLIPSIS

اگر خروجی شامل آدرس حافظه یا بخشی باشد که اهمیت ندارد، از ... به همراه فلگ ELLIPSIS استفاده می‌کنیم.

ب) مدیریت فاصله‌ها با NORMALIZE_WHITESPACE

پایتون به فاصله‌ها حساس است. اگر خروجی شما شامل چندین خط یا فاصله‌های اضافی باشد، این فلگ کمک می‌کند تا تفاوت‌های whitespace نادیده گرفته شوند.

مثال جامع حرفه‌ای: کلاس با خروجی پیچیده

Python

۲. تست دیکشنری‌ها و ترتیب (Best Practices)

دیکشنری‌ها در نسخه‌های قدیمی پایتون ترتیب مشخصی نداشتند، و حتی در نسخه‌های جدید برای تست بهتر است به ترتیب کلیدها اعتماد نکنید یا خروجی را سورت کنید. doctest متن خروجی را دقیقاً کاراکتر به کاراکتر مقایسه می‌کند.

مثال تکنیکی:

Python

۳. تست کردن فایل‌های متنی جداگانه (testfile)

در پروژه‌های بزرگ، ممکن است نخواهید کد و تست در یک فایل باشند. می‌توانید سناریوها را در یک فایل متنی (مثلاً scenario.txt) بنویسید و با doctest.testfile اجرا کنید.

تصویر زیر ساختار پیشنهادی برای جدا سازی فایل‌های تست را نشان می‌دهد:

ساختار فایل‌های تست

مثال کد اجرایی برای تست فایل متنی:

این اسکریپت فرض می‌کند فایلی متنی حاوی تست‌ها وجود دارد، اما چون فایل خارجی نداریم، مکانیزم آن را با ساخت یک فایل موقت شبیه‌سازی می‌کنیم تا مثال Self-Executable باقی بماند.

Python

۴. نکات فنی و اجتناب از دام‌ها (Gotchas)

  • خروجی‌های خالی: اگر تابعی None برگرداند، در کنسول چیزی چاپ نمی‌شود. در doctest نباید خط خالی بگذارید مگر اینکه واقعاً خط خالی چاپ شود. برای تست None بهتر است صراحتاً print() کنید یا از is None استفاده کنید.
  • بک‌اسلش‌ها: در رشته‌های معمولی پایتون، \ کاراکتر فرار است. در داک‌استرینگ‌ها (اگر r""" نباشد) باید از \\ استفاده کنید.
python
def path_helper():
    r"""
    استفاده از Raw String (r"...") برای مسیرهای ویندوزی توصیه می‌شود.
    
    >>> print(r"C:\Windows\System32")
    C:\Windows\System32
    """
    pass