doctest --- آزمون مثال‌های تعاملی پایتون

کد منبع: Lib/doctest.py


ماژول doctest به دنبال تکه‌هایی از متن می‌گردد که شبیه نشست‌های تعاملی پایتون هستند، سپس آن نشست‌ها را اجرا می‌کند تا تأیید کند که دقیقاً همان‌طور که نشان داده شده‌اند کار می‌کنند. چندین روش رایج برای استفاده از doctest وجود دارد:

  • برای بررسی به‌روز بودن رشته مستندات‌های یک ماژول با صحت‌سنجی اینکه همه مثال‌های تعاملی هنوز مطابق مستندات کار می‌کنند.

  • برای انجام آزمون رگرسیون با صحت‌سنجی اینکه مثال‌های تعاملی از یک پرونده آزمون یا یک شیء آزمون همان‌طور که انتظار می‌رود کار می‌کنند.

  • برای نوشتن مستندات آموزشی یک بسته، که به‌وفور با مثال‌های ورودی-خروجی همراه شده است. بسته به این‌که مثال‌ها یا متن توضیحی بیشتر مورد تأکید باشند، این حالت، حال‌وهوای «آزمون باسواد» (literate testing) یا «مستندات اجرایی» (executable documentation) را دارد.

در اینجا یک ماژول نمونه‌ی کامل اما کوچک آمده است:

"""
This is the "example" module.

The example module supplies one function, factorial().  For example,

>>> factorial(5)
120
"""

def factorial(n):
    """Return the factorial of n, an exact integer >= 0.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(30)
    265252859812191058636308480000000
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:
    >>> factorial(30.1)
    Traceback (most recent call last):
        ...
    ValueError: n must be exact integer
    >>> factorial(30.0)
    265252859812191058636308480000000

    It must also not be ridiculously large:
    >>> factorial(1e100)
    Traceback (most recent call last):
        ...
    OverflowError: n too large
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # catch a value like 1e300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result


if __name__ == "__main__":
    import doctest
    doctest.testmod()

اگر example.py را مستقیماً از خط فرمان اجرا کنید، doctest جادوی خود را انجام می‌دهد:

$ python example.py
$

هیچ خروجی‌ای وجود ندارد! این طبیعی است و به این معناست که همه‌ی مثال‌ها کار کرده‌اند. آرگومان -v را به اسکریپت بدهید، و doctest گزارش دقیقی از آنچه آزمایش می‌کند چاپ می‌کند و در پایان خلاصه‌ای چاپ می‌کند:

$ python example.py -v
Trying:
    factorial(5)
Expecting:
    120
ok
Trying:
    [factorial(n) for n in range(6)]
Expecting:
    [1, 1, 2, 6, 24, 120]
ok

و به همین ترتیب، که در نهایت با … پایان می‌یابد:

Trying:
    factorial(1e100)
Expecting:
    Traceback (most recent call last):
        ...
    OverflowError: n too large
ok
2 items passed all tests:
   1 test in __main__
   6 tests in __main__.factorial
7 tests in 2 items.
7 passed.
Test passed.
$

این تمام چیزی است که برای شروع استفاده‌ی مؤثر از doctest نیاز دارید بدانید! شروع کنید. بخش‌های زیر جزئیات کامل را ارائه می‌دهند. توجه داشته باشید که نمونه‌های doctest زیادی در بدنه‌ی آزمون و کتابخانه‌های استاندارد پایتون وجود دارد. نمونه‌های به‌ویژه مفید را می‌توان در پرونده‌ی آزمون استاندارد Lib/test/test_doctest/test_doctest.py یافت.

اضافه شده در نسخه‌ی 3.13: خروجی به‌طور پیش‌فرض رنگی است و می‌تواند با استفاده از متغیرهای محیطی کنترل شود.

استفاده ساده: بررسی مثال‌ها در رشته مستندات

ساده‌ترین روش برای شروع استفاده از doctest (اما نه لزوماً روشی که به آن ادامه خواهید داد) این است که در پایان هر ماژول M این را قرار دهید:

if __name__ == "__main__":
    import doctest
    doctest.testmod()

doctest سپس رشته مستندات را در ماژول M بررسی می‌کند.

اجرای ماژول به‌عنوان یک اسکریپت باعث می‌شود مثال‌های موجود در رشته مستندات اجرا و تأیید شوند:

python M.py

این هیچ چیزی را نمایش نمی‌دهد، مگر اینکه مثالی شکست بخورد؛ در این صورت، مثال‌های شکست‌خورده و علت‌های شکست در stdout چاپ می‌شوند و آخرین خط خروجی ***Test Failed*** N failures. است، که در آن N تعداد مثال‌هایی است که شکست خورده‌اند.

در عوض آن را با گزینه -v اجرا کنید:

python M.py -v

و گزارش دقیقی از همه مثال‌های آزمایش‌شده در خروجی استاندارد چاپ می‌شود، به‌همراه خلاصه‌های گوناگونی در پایان.

می‌توانید با گذراندن verbose=True به testmod()، حالت پرگو (verbose mode) را تحمیل کنید، یا با گذراندن verbose=False از آن جلوگیری کنید. در هر یک از این موارد، sys.argv توسط testmod() بررسی نمی‌شود (بنابراین گذراندن -v یا نگذراندن آن تأثیری ندارد).

همچنین یک میان‌بر خط فرمان برای اجرای testmod() وجود دارد، بخش استفاده از خط فرمان را ببینید.

برای اطلاعات بیشتر درباره testmod()، بخش API پایه را ببینید.

استفاده ساده: بررسی مثال‌ها در یک پرونده متنی

یکی دیگر از کاربردهای ساده‌ی doctest، آزمایش مثال‌های تعاملی در یک پرونده متنی است. این کار را می‌توان با تابع testfile() انجام داد:

import doctest
doctest.testfile("example.txt")

آن اسکریپت کوتاه، هر مثال تعاملی پایتون موجود در پرونده example.txt را اجرا و صحت‌سنجی می‌کند. محتوای پرونده به‌عنوان یک رشته‌ی مستندسازی بزرگ و واحد در نظر گرفته می‌شود؛ لازم نیست پرونده حاوی یک برنامه‌ی پایتون باشد! برای مثال، ممکن است example.txt شامل این باشد:

ماژول ``example``
======================

استفاده از ``factorial``
--------------------------

این یک پرونده متنی نمونه در قالب reStructuredText است. ابتدا ``factorial`` را
از ماژول ``example`` ایمپورت کنید:

    >>> from example import factorial

اکنون از آن استفاده کنید:

    >>> factorial(6)
    120

سپس با اجرای doctest.testfile("example.txt") خطا در این مستندات پیدا می‌شود:

File "./example.txt", line 14, in example.txt
Failed example:
    factorial(6)
Expected:
    120
Got:
    720

مانند testmod()، testfile() نیز چیزی نمایش نمی‌دهد مگر آنکه مثالی شکست بخورد. اگر مثالی شکست بخورد، مثال(های) شکست‌خورده و علت(های) شکست با همان قالب testmod() در stdout چاپ می‌شوند.

به‌طور پیش‌فرض، testfile() در پوشه‌ی ماژول فراخوان به دنبال پرونده‌ها می‌گردد. برای توضیح آرگومان‌های اختیاری که می‌توان از آن‌ها استفاده کرد تا به آن گفته شود پرونده‌ها را در مکان‌های دیگر جستجو کند، بخش API پایه را ببینید.

مانند testmod()، میزان پرگویی testfile() را می‌توان با گزینه خط فرمان -v یا آرگومان کلیدواژه‌ای اختیاری verbose تنظیم کرد.

همچنین یک میان‌بر خط فرمان برای اجرای testfile() وجود دارد، بخش استفاده از خط فرمان را ببینید.

برای اطلاعات بیشتر درباره testfile()، بخش API پایه را ببینید.

استفاده از خط فرمان

ماژول doctest را می‌توان به‌عنوان یک اسکریپت از خط فرمان فراخوانی کرد:

python -m doctest [-v] [-o OPTION] [-f] file [file ...]
-v, --verbose

گزارش دقیقی از تمام مثال‌های آزمایش‌شده در خروجی استاندارد چاپ می‌شود، به همراه خلاصه‌های گوناگونی در پایان:

python -m doctest -v example.py

این کار example.py را به‌عنوان یک ماژول مستقل ایمپورت می‌کند و testmod() را روی آن اجرا می‌کند. توجه داشته باشید که اگر پرونده بخشی از یک بسته باشد و زیرماژول‌های دیگری را از آن بسته ایمپورت کند، ممکن است این کار به‌درستی انجام نشود.

اگر نام پرونده به .py ختم نشود، doctest استنباط می‌کند که باید در عوض با testfile() اجرا شود:

python -m doctest -v example.txt
-o, --option <option>

پرچم‌های گزینه، جنبه‌های گوناگون رفتار doctest را کنترل می‌کنند؛ بخش پرچم‌های گزینه را ببینید.

اضافه شده در نسخه‌ی 3.4.

-f, --fail-fast

این میان‌بری برای -o FAIL_FAST است.

اضافه شده در نسخه‌ی 3.4.

چگونه کار می‌کند

این بخش با جزئیات بررسی می‌کند که doctest چگونه کار می‌کند: به کدام رشته‌مستندها نگاه می‌کند، چگونه مثال‌های تعاملی را پیدا می‌کند، از چه زمینه اجرا استفاده می‌کند، چگونه استثناها را مدیریت می‌کند، و چگونه می‌توان از پرچم‌های گزینه برای کنترل رفتار آن استفاده کرد. این اطلاعاتی است که شما برای نوشتن مثال‌های doctest باید بدانید؛ برای اطلاع از اجرای واقعی doctest روی این مثال‌ها، بخش‌های زیر را ببینید.

کدام رشته مستندات بررسی می‌شوند؟

رشته مستندات ماژول و تمام رشته مستندات تابع، کلاس و متد جستجو می‌شوند. اشیایی که به ماژول ایمپورت شده‌اند، جستجو نمی‌شوند.

علاوه بر این، مواردی وجود دارد که می‌خواهید آزمون‌ها بخشی از یک ماژول باشند اما بخشی از متن راهنما نباشند، که این مستلزم آن است که آزمون‌ها در رشته مستند گنجانده نشوند. Doctest به دنبال یک متغیر سطح ماژول به نام __test__ می‌گردد و از آن برای یافتن سایر آزمون‌ها استفاده می‌کند. اگر M.__test__ وجود داشته باشد، باید یک دیکشنری باشد، و هر آیتم یک نام (رشته‌ای) را به یک شیء تابع، شیء کلاس یا رشته نگاشت می‌کند. رشته مستندات اشیاء تابع و کلاس که از M.__test__ یافت می‌شوند، جستجو می‌شوند، و رشته‌ها طوری رفتار می‌شوند که گویی رشته مستند هستند. در خروجی، یک کلید K در M.__test__ با نام M.__test__.K ظاهر می‌شود.

برای مثال، این قطعه کد را در ابتدای example.py قرار دهید:

__test__ = {
    'numbers': """
>>> factorial(6)
720

>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
"""
}

مقدار example.__test__["numbers"] به‌عنوان یک رشته مستندسازی در نظر گرفته می‌شود و همه‌ی آزمون‌های داخل آن اجرا خواهند شد. توجه داشته باشید که این مقدار می‌تواند به یک تابع، شیء کلاس یا ماژول نگاشته شود؛ در این صورت، doctest آن‌ها را به‌صورت بازگشتی برای یافتن رشته مستندات جستجو می‌کند و سپس این رشته‌ها برای یافتن آزمون‌ها پویش می‌شوند.

هر کلاس یافت‌شده به‌طور بازگشتی به‌همین ترتیب جستجو می‌شود تا رشته‌مستندها در متدها و کلاس‌های تودرتوی درون آن آزمایش شوند.

توجه

doctest تنها می‌تواند به‌صورت خودکار کلاس‌ها و توابعی را که در سطح ماژول یا درون کلاس‌های دیگر تعریف شده‌اند، کشف کند.

از آنجا که کلاس‌ها و توابع تودرتو تنها زمانی وجود دارند که یک تابع بیرونی فراخوانی شود، نمی‌توان آن‌ها را کشف کرد. آن‌ها را بیرون تعریف کنید تا قابل مشاهده شوند.

مثال‌های رشته‌ی مستندسازی چگونه شناسایی می‌شوند؟

در بیشتر موارد، کپی-پیست یک نشست کنسول تعاملی به‌خوبی کار می‌کند، اما doctest در پی شبیه‌سازی دقیق هیچ پوسته‌ی خاصی از پایتون نیست.

>>> # comments are ignored
>>> x = 12
>>> x
12
>>> if x == 13:
...     print("yes")
... else:
...     print("no")
...     print("NO")
...     print("NO!!!")
...
no
NO
NO!!!
>>>

هر خروجی مورد انتظار باید بلافاصله پس از آخرین خط '>>> ' یا '... ' حاوی کد بیاید، و خروجی مورد انتظار (در صورت وجود) تا خط '>>> ' بعدی یا خط فقط شامل فضای خالی ادامه می‌یابد.

نکات ریز:

  • خروجی مورد انتظار نمی‌تواند شامل سطری باشد که فقط فضای خالی است، زیرا چنین سطری به‌عنوان نشانه پایان خروجی مورد انتظار در نظر گرفته می‌شود. اگر خروجی مورد انتظار شامل یک خط خالی است، در مثال doctest خود، در هر جایی که یک خط خالی انتظار می‌رود، <BLANKLINE> قرار دهید.

  • همه‌ی نویسه‌های تب سخت با استفاده از محل‌های توقف تب ۸ ستونه به فاصله‌ها تبدیل می‌شوند. تب‌های موجود در خروجی تولیدشده به‌وسیله‌ی کد آزمایش‌شده تغییر نمی‌کنند. از آنجا که هر تب سختی در خروجی نمونه تبدیل می‌شود، این بدان معناست که اگر خروجی کد شامل تب‌های سخت باشد، تنها راه قبولی doctest این است که گزینه‌ی NORMALIZE_WHITESPACE یا دایرکتیو فعال باشد. به‌عنوان جایگزین، می‌توان آزمون را بازنویسی کرد تا خروجی را ضبط کند و آن را با یک مقدار مورد انتظار به‌عنوان بخشی از آزمون مقایسه کند. این نحوه‌ی مدیریت تب‌ها در منبع از طریق آزمون و خطا به دست آمده و نشان داده است که کم‌خطاترین روش برای مدیریت آن‌ها است. با نوشتن یک کلاس DocTestParser سفارشی، می‌توان از الگوریتم متفاوتی برای مدیریت تب‌ها استفاده کرد.

  • خروجی به stdout ضبط می‌شود، اما خروجی به stderr ضبط نمی‌شود (ردگیری‌های پشته‌ی استثنا از روش دیگری ضبط می‌شوند).

  • اگر در یک نشست تعاملی سطری را با بک‌اسلش ادامه می‌دهید، یا به هر دلیل دیگری از بک‌اسلش استفاده می‌کنید، باید از یک رشته مستند خام استفاده کنید، که بک‌اسلش‌های شما را دقیقاً همان‌گونه که تایپ می‌کنید حفظ می‌کند:

    >>> def f(x):
    ...     r'''Backslashes in a raw docstring: m\n'''
    ...
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    

    در غیر این صورت، بک‌اسلش به‌عنوان بخشی از رشته تفسیر می‌شود. برای مثال، \n بالا به‌عنوان یک نویسه‌ی خط جدید تفسیر می‌شود. به‌عنوان جایگزین، می‌توانید هر بک‌اسلش را در نسخه‌ی doctest دو بار تکرار کنید (و از یک رشته‌ی خام استفاده نکنید):

    >>> def f(x):
    ...     '''Backslashes in a raw docstring: m\\n'''
    ...
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    
  • ستون آغازین اهمیتی ندارد:

    >>> assert "Easy!"
          >>> import math
              >>> math.floor(1.9)
              1
    

    و به همان تعداد نویسه‌ی فضای سفید ابتدایی که در خط نخست '>>> ' آغازگر مثال ظاهر شده است، از خروجی مورد انتظار حذف می‌شود.

زمینه اجرا (Execution Context) چیست؟

به‌طور پیش‌فرض، هر بار که doctest رشته مستندی برای آزمون پیدا می‌کند، از یک کپی کم‌عمق از سراسری‌های M استفاده می‌کند، تا اجرای آزمون‌ها سراسری‌های واقعی ماژول را تغییر ندهد و تا یک آزمون در M نتواند بقایایی بر جای بگذارد که به‌طور تصادفی موجب کار کردن آزمون دیگری شود. این بدان معناست که مثال‌ها می‌توانند آزادانه از هر نامی که در سطح بالای M تعریف شده است و از نام‌هایی که پیش‌تر در رشته مستندی که در حال اجرا است تعریف شده‌اند، استفاده کنند. مثال‌ها نمی‌توانند نام‌های تعریف‌شده در رشته مستندات‌های دیگر را ببینند.

در عوض، می‌توانید با ارسال globs=your_dict به testmod() یا testfile()، استفاده از دیکشنری خودتان را به‌عنوان زمینه اجرا تحمیل کنید.

در مورد استثناها چطور؟

مشکلی نیست، به شرطی که ردگیری پشته تنها خروجی تولیدشده توسط مثال باشد: فقط ردگیری پشته را بچسبانید. [1] از آنجا که ردگیری‌های پشته حاوی جزئیاتی هستند که احتمالاً به‌سرعت تغییر می‌کنند (برای مثال، مسیرهای دقیق پرونده و شماره سطرهای)، این یکی از مواردی است که doctest تلاش زیادی می‌کند تا نسبت به آنچه می‌پذیرد انعطاف‌پذیر باشد.

مثال ساده:

>>> [1, 2, 3].remove(42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list

آن doctest در صورتی موفق می‌شود که ValueError پرتاب شود، با جزئیات list.remove(x): x not in list همان‌طور که نشان داده شده است.

خروجی مورد انتظار برای یک استثنا باید با یک سرآیند ردگیری پشته شروع شود؛ این سرآیند می‌تواند یکی از دو خط زیر باشد و تورفتگی آن باید مشابه خط اول مثال باشد:

Traceback (most recent call last):
Traceback (innermost last):

پس از سرآیند ردگیری پشته، یک پشته‌ی ردگیری اختیاری می‌آید که محتوای آن توسط doctest نادیده گرفته می‌شود. پشته‌ی ردگیری معمولاً حذف می‌شود، یا عیناً از یک نشست تعاملی کپی می‌شود.

پس از پشته‌ی ردگیری، جالب‌ترین بخش می‌آید: خط(ها)ی حاوی نوع استثنا و جزئیات آن. این معمولاً آخرین خط یک ردگیری است، اما اگر استثنا دارای جزئیات چندخطی باشد، می‌تواند چندین خط را در بر بگیرد:

>>> raise ValueError('multi\n    line\ndetail')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: multi
    line
detail

سه خط آخر (که با ValueError شروع می‌شوند) با نوع و جزئیات استثنا مقایسه می‌شوند، و بقیه نادیده گرفته می‌شوند.

بهترین روش این است که ردگیری پشته را حذف کنید، مگر اینکه ارزش مستندسازی قابل‌توجهی به مثال ببخشد. بنابراین احتمالاً مثال آخر بهتر است به این صورت باشد:

>>> raise ValueError('multi\n    line\ndetail')
Traceback (most recent call last):
    ...
ValueError: multi
    line
detail

توجه داشته باشید که با ردگیری‌های پشته برخورد بسیار ویژه‌ای می‌شود. به‌ویژه، در مثال بازنویسی‌شده، استفاده از ... مستقل از گزینه‌ی ELLIPSIS در doctest است. سه‌نقطه‌ی آن مثال می‌تواند حذف شود، یا به‌همان‌خوبی می‌تواند سه (یا سیصد) کاما یا رقم، یا یک متن دارای تورفتگی از یک قطعه‌ی کمدی مانتی پایتون باشد.

برخی جزئیات را باید یک بار بخوانید، اما نیازی به یادآوری آن‌ها نخواهید داشت:

  • Doctest نمی‌تواند حدس بزند که خروجی مورد انتظار شما از ردگیری پشته‌ی یک استثنا آمده است یا از چاپ معمولی. بنابراین، برای مثال، مثالی که انتظار ValueError: 42 is prime را دارد، قبول می‌شود؛ چه ValueError واقعاً پرتاب شود و چه مثال صرفاً همان متن ردگیری پشته را چاپ کند. در عمل، خروجی معمولی به‌ندرت با یک خط سرآیند ردگیری پشته آغاز می‌شود، بنابراین این موضوع مشکل واقعی ایجاد نمی‌کند.

  • هر خط از ردگیری پشته (در صورت وجود) باید بیشتر از خط اول مثال تورفتگی داشته باشد، یا با یک نویسه‌ی غیرحرفی‌عددی شروع شود. نخستین خط پس از سرآیند ردگیری پشته که تورفتگی یکسانی داشته باشد و با یک حرف یا عدد شروع شود، به‌عنوان آغاز جزئیات استثنا در نظر گرفته می‌شود. البته این برای ردگیری‌های پشته‌ی واقعی به‌درستی عمل می‌کند.

  • هنگامی که گزینه‌ی doctest با نام IGNORE_EXCEPTION_DETAIL مشخص شود، هر آنچه پس از چپ‌ترین دونقطه بیاید و هرگونه اطلاعات ماژول در نام استثنا نادیده گرفته می‌شود.

  • پوسته تعاملی خط سرآیند ردگیری پشته را برای برخی از استثناهای SyntaxError حذف می‌کند. اما doctest از خط سرآیند ردگیری پشته برای تمایز استثناها از غیراستثناها استفاده می‌کند. بنابراین در موارد نادری که نیاز دارید یک SyntaxError را که فاقد خط سرآیند ردگیری پشته است آزمون کنید، لازم است خط سرآیند ردگیری پشته را به‌صورت دستی به مثال آزمون خود اضافه کنید.

  • برای برخی استثناها، پایتون موقعیت خطا را با نشانگرهای ^ و تیلداها نمایش می‌دهد:

    >>> 1 + None
      File "<stdin>", line 1
        1 + None
        ~~^~~~~~
    TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
    

    از آنجا که سطرهایی که موقعیت خطا را نشان می‌دهند پیش از نوع استثنا و جزئیات آن می‌آیند، توسط doctest بررسی نمی‌شوند. برای مثال، آزمون زیر قبول می‌شود، حتی اگر نشانگر ^ را در مکان نادرست قرار دهد:

    >>> 1 + None
      File "<stdin>", line 1
        1 + None
        ^~~~~~~~
    TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
    

پرچم‌های گزینه

تعدادی از پرچم‌های گزینه، جنبه‌های مختلف رفتار doctest را کنترل می‌کنند. نام‌های نمادین این پرچم‌ها به‌عنوان ثابت‌های ماژول ارائه شده‌اند و می‌توان آن‌ها را با هم به‌صورت OR بیتی ترکیب کرد و به توابع مختلف فرستاد. این نام‌ها همچنین می‌توانند در دایرکتیوهای doctest استفاده شوند و از طریق گزینه -o به رابط خط فرمان doctest فرستاده شوند.

اولین گروه از گزینه‌ها، معنای آزمون را تعریف می‌کنند و جنبه‌هایی از چگونگی تصمیم‌گیری doctest در مورد این‌که آیا خروجی واقعی با خروجی مورد انتظار یک مثال مطابقت دارد یا نه را کنترل می‌کنند:

doctest.DONT_ACCEPT_TRUE_FOR_1

به‌طور پیش‌فرض، اگر یک بلوک خروجی مورد انتظار فقط شامل 1 باشد، بلوک خروجی واقعی که فقط شامل 1 یا فقط True باشد، به‌عنوان تطابق در نظر گرفته می‌شود، و به‌همین ترتیب برای 0 در برابر False نیز چنین است. هنگامی که DONT_ACCEPT_TRUE_FOR_1 مشخص شده باشد، هیچ‌یک از این جایگزینی‌ها مجاز نیست. رفتار پیش‌فرض با این واقعیت سازگار است که پایتون نوع بازگشتی بسیاری از توابع را از عدد صحیح به بولی تغییر داده است؛ doctestهایی که انتظار خروجی «عدد صحیح کوچک» دارند، در این موارد همچنان کار می‌کنند. این گزینه احتمالاً حذف خواهد شد، اما نه برای چندین سال.

doctest.DONT_ACCEPT_BLANKLINE

به‌طور پیش‌فرض، اگر یک بلوک خروجی مورد انتظار شامل سطری باشد که فقط شامل رشته‌ی <BLANKLINE> است، آن خط با یک خط خالی در خروجی واقعی مطابقت خواهد داشت. از آنجا که یک خط خالی واقعی پایان خروجی مورد انتظار را مشخص می‌کند، این تنها راه برای اعلام این است که یک خط خالی مورد انتظار است. هنگامی که DONT_ACCEPT_BLANKLINE مشخص شده باشد، این جایگزینی مجاز نیست.

doctest.NORMALIZE_WHITESPACE

در صورت مشخص شدن، تمام دنباله‌های فضای سفید (فاصله‌ها و سطرهای جدید) یکسان در نظر گرفته می‌شوند. هر دنباله‌ای از فضای سفید در خروجی مورد انتظار با هر دنباله‌ای از فضای سفید در خروجی واقعی مطابقت خواهد داشت. به‌طور پیش‌فرض، فضای سفید باید دقیقاً مطابقت داشته باشد. NORMALIZE_WHITESPACE به‌ویژه زمانی مفید است که یک خط از خروجی مورد انتظار بسیار طولانی باشد و بخواهید آن را در کد منبع خود در چند خط بشکنید.

doctest.ELLIPSIS

در صورت مشخص شدن، نشانگر سه‌نقطه (...) در خروجی مورد انتظار می‌تواند با هر زیررشته‌ای در خروجی واقعی مطابقت کند. این شامل زیررشته‌هایی می‌شود که از مرزهای سطرهای می‌گذرند، و شامل زیررشته‌های خالی نیز می‌شود؛ بنابراین بهتر است استفاده از آن را ساده نگه دارید. استفاده‌های پیچیده می‌توانند به همان انواع غافلگیری‌های «ای وای، بیش از حد مطابقت کرد!» منجر شوند که .* در عبارات باقاعده مستعد ایجاد آن‌هاست.

doctest.IGNORE_EXCEPTION_DETAIL

در صورت مشخص شدن، doctestهایی که انتظار استثنا دارند، تا زمانی که استثنایی از نوع مورد انتظار پرتاب شود، موفق می‌شوند، حتی اگر جزئیات (پیام و نام کامل استثنا) مطابقت نداشته باشند.

برای مثال، مثالی که انتظار ValueError: 42 را دارد، در صورتی موفق می‌شود که استثنای واقعی پرتاب‌شده ValueError: 3*14 باشد، اما اگر، مثلاً، به‌جای آن استثنای TypeError پرتاب شود، ناموفق خواهد بود. همچنین هر نام کاملاً مشخصی را که پیش از کلاس استثنا آمده باشد نادیده می‌گیرد، که این نام ممکن است بین پیاده‌سازی‌ها و نسخه‌های پایتون و کد/کتابخانه‌های در حال استفاده متفاوت باشد. بنابراین، هر سه مورد از این حالت‌ها با پرچم مشخص‌شده کار خواهند کرد:

>>> raise Exception('message')
Traceback (most recent call last):
Exception: message

>>> raise Exception('message')
Traceback (most recent call last):
builtins.Exception: message

>>> raise Exception('message')
Traceback (most recent call last):
__main__.Exception: message

توجه داشته باشید که می‌توان از ELLIPSIS همچنین برای نادیده گرفتن جزئیات پیام استثنا استفاده کرد، اما چنین آزمونی ممکن است همچنان بسته به اینکه نام ماژول موجود باشد یا دقیقاً مطابقت داشته باشد، شکست بخورد.

تغییر یافته در نسخه‌ی 3.2: IGNORE_EXCEPTION_DETAIL اکنون هرگونه اطلاعات مربوط به ماژول حاوی استثنای در حال آزمون را نیز نادیده می‌گیرد.

doctest.SKIP

هنگامی که مشخص شده باشد، مثال به‌هیچ‌وجه اجرا نمی‌شود. این می‌تواند در زمینه‌هایی مفید باشد که مثال‌های doctest هم به‌عنوان مستندات و هم به‌عنوان موارد آزمون به کار می‌روند و باید مثالی برای اهداف مستندسازی گنجانده شود، اما نباید بررسی شود. برای مثال، ممکن است خروجی مثال تصادفی باشد؛ یا مثال ممکن است به منابعی وابسته باشد که برای راه‌انداز آزمون (test driver) در دسترس نباشند.

همچنین می‌توان از پرچم SKIP برای «کامنت کردن» موقت مثال‌ها استفاده کرد.

doctest.COMPARISON_FLAGS

یک نقاب بیتی (bitmask) حاصل از ترکیب همه‌ی پرچم‌های مقایسه‌ی بالا با عملگر OR.

گروه دوم گزینه‌ها، نحوه گزارش شکست آزمون‌ها را کنترل می‌کند:

doctest.REPORT_UDIFF

در صورت مشخص شدن، شکست‌هایی که شامل خروجی‌های مورد انتظار و واقعی چندخطی هستند، با استفاده از تفاوت یکپارچه (unified diff) نمایش داده می‌شوند.

doctest.REPORT_CDIFF

در صورت مشخص شدن، شکست‌هایی که خروجی‌های مورد انتظار و واقعی آن‌ها چندخطی هستند، با استفاده از diff زمینه‌ای (context diff) نمایش داده می‌شوند.

doctest.REPORT_NDIFF

در صورت تعیین شدن، تفاوت‌ها توسط difflib.Differ و با استفاده از همان الگوریتم ابزار محبوب ndiff.py محاسبه می‌شوند. این تنها متدی است که تفاوت‌ها را هم درون سطرهای و هم میان سطرهای علامت‌گذاری می‌کند. برای مثال، اگر یک خط از خروجی مورد انتظار شامل رقم 1 باشد در حالی که خروجی واقعی شامل حرف l باشد، سطری درج می‌شود که با علامت ^ موقعیت‌های ستونی ناهمخوان را مشخص می‌کند.

doctest.REPORT_ONLY_FIRST_FAILURE

در صورت تعیین شدن، نخستین مثال ناموفق در هر doctest نمایش داده می‌شود، اما خروجی تمام مثال‌های باقی‌مانده سرکوب می‌شود. این کار مانع از آن می‌شود که doctest مثال‌های درستی را گزارش کند که به دلیل شکست‌های پیشین خراب می‌شوند؛ اما ممکن است مثال‌های نادرستی را نیز پنهان کند که مستقل از نخستین شکست، شکست می‌خورند. هنگامی که REPORT_ONLY_FIRST_FAILURE تعیین شود، مثال‌های باقی‌مانده همچنان اجرا می‌شوند و همچنان در تعداد کل شکست‌های گزارش‌شده لحاظ می‌شوند؛ فقط خروجی سرکوب می‌شود.

doctest.FAIL_FAST

در صورت مشخص شدن، پس از نخستین مثال شکست‌خورده خارج می‌شود و برای اجرای مثال‌های باقی‌مانده تلاشی نمی‌کند. بنابراین، تعداد شکست‌های گزارش‌شده حداکثر ۱ خواهد بود. این پرچم ممکن است در حین اشکال‌زدایی مفید باشد، زیرا مثال‌های پس از نخستین شکست حتی خروجی اشکال‌زدایی تولید نمی‌کنند.

doctest.REPORTING_FLAGS

نقاب بیتی (bitmask) که همه‌ی پرچم‌های گزارش‌دهی بالا را با OR بیتی ترکیب می‌کند.

همچنین راهی برای ثبت نام‌های جدید پرچم‌های گزینه وجود دارد، هرچند این کار مفید نیست مگر اینکه قصد داشته باشید درونی‌های doctest را از طریق زیرکلاس‌سازی گسترش دهید:

doctest.register_optionflag(name)

یک پرچم گزینه جدید با نام داده‌شده ایجاد می‌کند و مقدار عدد صحیح پرچم جدید را برمی‌گرداند. می‌توان از register_optionflag() هنگام زیرکلاس‌سازی از OutputChecker یا DocTestRunner برای ایجاد گزینه‌های جدیدی که توسط زیرکلاس‌های شما پشتیبانی می‌شوند استفاده کرد. register_optionflag() باید همیشه با استفاده از الگوی زیر فراخوانی شود:

MY_FLAG = register_optionflag('MY_FLAG')

دایرکتیوها

می‌توان از دایرکتیوهای doctest برای تغییر پرچم‌های گزینه برای یک مثال منفرد استفاده کرد. دایرکتیوهای doctest، کامنت‌های ویژه پایتون هستند که پس از کد منبع یک مثال می‌آیند:

directive:             "#" "doctest:" directive_options
directive_options:     directive_option ("," directive_option)*
directive_option:      on_or_off directive_option_name
on_or_off:             "+" | "-"
directive_option_name: "DONT_ACCEPT_BLANKLINE" | "NORMALIZE_WHITESPACE" | ...

فضای سفید میان + یا - و نام گزینه‌ی دایرکتیو مجاز نیست. نام گزینه‌ی دایرکتیو می‌تواند هر یک از نام‌های پرچم گزینه باشد که در بالا توضیح داده شده‌اند.

دایرکتیوهای doctestِ یک مثال، رفتار doctest را برای همان یک مثال تغییر می‌دهند. برای فعال کردن رفتار نام‌برده از + و برای غیرفعال کردن آن از - استفاده کنید.

برای مثال، این آزمون قبول می‌شود:

>>> print(list(range(20)))  # doctest: +NORMALIZE_WHITESPACE
[0,   1,  2,  3,  4,  5,  6,  7,  8,  9,
10,  11, 12, 13, 14, 15, 16, 17, 18, 19]

بدون دایرکتیو (directive) این آزمون شکست می‌خورد، هم به این دلیل که خروجی واقعی پیش از المان‌های فهرست تک‌رقمی ۲ فاصله‌ی خالی ندارد، و هم به این دلیل که خروجی واقعی در یک خط واحد قرار دارد. این آزمون نیز قبول می‌شود، و برای این کار نیز به یک دایرکتیو نیاز دارد:

>>> print(list(range(20)))  # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]

می‌توان چندین دایرکتیو (directives) را در یک خط فیزیکی واحد استفاده کرد، که با کاما از هم جدا شده‌اند:

>>> print(list(range(20)))  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0,    1, ...,   18,    19]

اگر برای یک مثال از چندین کامنت دایرکتیو (directive comments) استفاده شود، آن‌ها با هم ترکیب می‌شوند:

>>> print(list(range(20)))  # doctest: +ELLIPSIS
...                         # doctest: +NORMALIZE_WHITESPACE
[0,    1, ...,   18,    19]

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

>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
... # doctest: +ELLIPSIS
[0, ..., 4, 10, ..., 19, 30, ..., 39]

توجه داشته باشید که چون همه‌ی گزینه‌ها به‌طور پیش‌فرض غیرفعال هستند و دایرکتیوها فقط بر مثالی که در آن قرار دارند اعمال می‌شوند، فعال کردن گزینه‌ها (از طریق + در یک دایرکتیو) معمولاً تنها انتخاب معنادار است. با این حال، پرچم‌های گزینه را نیز می‌توان به توابعی که doctestها را اجرا می‌کنند ارسال کرد و بدین ترتیب پیش‌فرض‌های متفاوتی برقرار ساخت. در چنین مواردی، غیرفعال کردن یک گزینه از طریق - در یک دایرکتیو می‌تواند مفید باشد.

هشدارها

doctest در الزام به تطابق دقیق در خروجی مورد انتظار سخت‌گیر است. اگر حتی یک نویسه مطابقت نداشته باشد، آزمون شکست می‌خورد. این موضوع احتمالاً چند بار شما را غافلگیر خواهد کرد، هنگامی که دقیقاً یاد می‌گیرید که پایتون درباره خروجی چه چیزی را تضمین می‌کند و چه چیزی را تضمین نمی‌کند. برای مثال، هنگام چاپ یک مجموعه، پایتون تضمین نمی‌کند که المان‌ها با ترتیب خاصی چاپ شوند، بنابراین آزمونی مانند

>>> foo()
{"spam", "eggs"}

آسیب‌پذیر است! یک راه‌حل موقت این است که

>>> foo() == {"spam", "eggs"}
True

در عوض. راه دیگر این است که آن را انجام دهید

>>> d = sorted(foo())
>>> d
['eggs', 'spam']

موارد دیگری نیز وجود دارند، اما منظور را متوجه می‌شوید.

ایده بد دیگر چاپ مواردی است که آدرس شیء در آن‌ها تعبیه شده‌است، مانند

>>> id(1.0)  # certain to fail some of the time
7948648
>>> class C: pass
>>> C()  # the default repr() for instances embeds an address
<C object at 0x00AC18F0>

دایرکتیو ELLIPSIS رویکرد مناسبی برای آخرین مثال ارائه می‌دهد:

>>> C()  # doctest: +ELLIPSIS
<C object at 0x...>

اعداد ممیز شناور نیز در پلتفرم‌های مختلف دچار تفاوت‌های کوچک در خروجی می‌شوند، زیرا پایتون برخی محاسبات ممیز شناور را به کتابخانه C پلتفرم واگذار می‌کند و کتابخانه‌های C از نظر کیفیت در این زمینه بسیار متفاوت هستند.

>>> 1000**0.1  # risky
1.9952623149688797
>>> round(1000**0.1, 9) # safer
1.995262315
>>> print(f'{1000**0.1:.4f}') # much safer
1.9953

اعدادی به شکل I/2.**J در همه‌ی سکوها ایمن هستند، و من اغلب مثال‌های doctest را به‌گونه‌ای طراحی می‌کنم که اعدادی به آن شکل تولید کنند:

>>> 3./4  # utterly safe
0.75

درک کسرهای ساده نیز برای افراد آسان‌تر است و این موضوع باعث بهتر شدن مستندات می‌شود.

API پایه

توابع testmod() و testfile() رابط ساده‌ای برای doctest فراهم می‌کنند که باید برای بیشتر استفاده‌های پایه کافی باشد. برای آشنایی غیررسمی‌تر با این دو تابع، بخش‌های استفاده ساده: بررسی مثال‌ها در رشته مستندات و استفاده ساده: بررسی مثال‌ها در یک پرونده متنی را ببینید.

doctest.testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None)

همه آرگومان‌ها به‌جز filename اختیاری هستند و باید به‌صورت آرگومان کلیدواژه‌ای مشخص شوند.

مثال‌های موجود در پرونده‌ای با نام filename را آزمون می‌کند. (failure_count, test_count) را برمی‌گرداند.

آرگومان اختیاری module_relative مشخص می‌کند که نام پرونده چگونه باید تفسیر شود:

  • اگر module_relative برابر True باشد (حالت پیش‌فرض)، آنگاه filename یک مسیر مستقل از سیستم‌عامل و نسبت به ماژول را مشخص می‌کند. به‌طور پیش‌فرض، این مسیر نسبت به پوشه‌ی ماژول فراخوان است؛ اما اگر آرگومان package مشخص شده باشد، نسبت به آن بسته است. برای اطمینان از استقلال از سیستم‌عامل، filename باید از نویسه‌های / برای جداسازی بخش‌های مسیر استفاده کند و نباید یک مسیر مطلق باشد (یعنی نباید با / شروع شود).

  • اگر module_relative برابر False باشد، آنگاه filename مسیری مختص سیستم‌عامل را مشخص می‌کند. این مسیر ممکن است مطلق یا نسبی باشد؛ مسیرهای نسبی با توجه به پوشه کاری فعلی تعیین می‌شوند.

آرگومان اختیاری name نام آزمون را مشخص می‌کند؛ به‌طور پیش‌فرض، یا اگر None باشد، از os.path.basename(filename) استفاده می‌شود.

آرگومان اختیاری package یک بسته پایتون یا نام یک بسته پایتون است که پوشه‌ی آن باید به‌عنوان پوشه‌ی پایه برای نام پرونده نسبی نسبت به ماژول استفاده شود. اگر بسته‌ای مشخص نشده باشد، پوشه‌ی ماژول فراخوان به‌عنوان پوشه‌ی پایه برای نام پرونده‌های نسبی نسبت به ماژول استفاده می‌شود. اگر module_relative برابر False باشد، مشخص کردن package خطا است.

آرگومان اختیاری globs یک دیکشنری برای استفاده به‌عنوان متغیرهای سراسری هنگام اجرای مثال‌ها مشخص می‌کند. یک رونوشت سطحی جدید از این دیکشنری برای doctest ایجاد می‌شود، بنابراین مثال‌های آن با وضعیتی تازه آغاز می‌شوند. به‌طور پیش‌فرض، یا اگر None باشد، از یک دیکشنری خالی جدید استفاده می‌شود.

آرگومان اختیاری extraglobs دیکشنری‌ای را ارائه می‌دهد که در متغیرهای سراسری استفاده‌شده برای اجرای مثال‌ها ادغام می‌شود. این مانند dict.update() عمل می‌کند: اگر globs و extraglobs کلید مشترکی داشته باشند، مقدار متناظر در extraglobs در دیکشنری ترکیب‌شده ظاهر می‌شود. به‌طور پیش‌فرض، یا اگر None باشد، از متغیرهای سراسری اضافی استفاده نمی‌شود. این یک قابلیت پیشرفته است که امکان پارامتری‌سازی doctestها را فراهم می‌کند. برای مثال، می‌توان یک doctest را برای یک کلاس پایه با استفاده از یک نام عام برای کلاس نوشت، و سپس با دادن یک دیکشنری extraglobs که نام عام را به زیرکلاس مورد آزمایش نگاشت می‌کند، از آن برای آزمایش هر تعداد زیرکلاس استفاده مجدد کرد.

آرگومان اختیاری verbose در صورت درست بودن، خروجی زیادی چاپ می‌کند و در صورت نادرست بودن، فقط شکست‌ها را چاپ می‌کند؛ به‌طور پیش‌فرض، یا اگر None باشد، مقدار آن درست است اگر و فقط اگر '-v' در sys.argv وجود داشته باشد.

آرگومان اختیاری report در صورت درست بودن، در پایان خلاصه‌ای چاپ می‌کند؛ در غیر این صورت، در پایان چیزی چاپ نمی‌کند. در حالت پرگو، خلاصه پرجزئیات است؛ در غیر این صورت، خلاصه بسیار مختصر است (در واقع اگر همه‌ی آزمون‌ها با موفقیت گذشتند، خالی است).

آرگومان اختیاری optionflags (مقدار پیش‌فرض 0) پرچم‌های گزینه را به‌صورت OR بیتی می‌پذیرد. بخش پرچم‌های گزینه را ببینید.

مقدار پیش‌فرض آرگومان اختیاری raise_on_error برابر false است. اگر true باشد، در اولین شکست یا استثنای غیرمنتظره در یک مثال، یک استثنا پرتاب می‌شود. این امکان را می‌دهد که شکست‌ها به‌صورت پس‌مرگ (post-mortem) اشکال‌زدایی شوند. رفتار پیش‌فرض، ادامه اجرای مثال‌ها است.

آرگومان اختیاری parser، یک DocTestParser (یا زیرکلاس) را مشخص می‌کند که باید برای استخراج آزمون‌ها از پرونده‌ها استفاده شود. مقدار پیش‌فرض آن یک پارسر معمولی است (یعنی DocTestParser()).

آرگومان اختیاری encoding، کدگذاری‌ای را که باید برای تبدیل پرونده به یونیکد استفاده شود، مشخص می‌کند.

doctest.testmod(m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False)

همه آرگومان‌ها اختیاری هستند، و همه به‌جز m باید به‌صورت آرگومان کلیدواژه‌ای مشخص شوند.

آزمایش مثال‌های موجود در رشته‌مستندها در توابع و کلاس‌های قابل‌دسترس از ماژول m (یا ماژول __main__ اگر m ارائه نشده باشد یا None باشد)، آغاز از m.__doc__.

همچنین مثال‌های قابل دسترس از دیکشنری m.__test__ را نیز، در صورت وجود، آزمون می‌کند. m.__test__ نام‌ها (رشته‌ها) را به توابع، کلاس‌ها و رشته‌ها نگاشت می‌کند؛ در رشته‌مستندهای توابع و کلاس‌ها برای یافتن مثال‌ها جستجو می‌شود؛ رشته‌ها مستقیماً جستجو می‌شوند، گویی رشته‌مستند هستند.

تنها رشته‌مستندها که به اشیای متعلق به ماژول m متصل هستند، جستجو می‌شوند.

(failure_count, test_count) را برمی‌گرداند.

آرگومان اختیاری name نام ماژول را مشخص می‌کند؛ به‌طور پیش‌فرض، یا اگر None باشد، از m.__name__ استفاده می‌شود.

آرگومان اختیاری exclude_empty به‌طور پیش‌فرض false است. اگر true باشد، اشیایی که برای آن‌ها هیچ doctestای یافت نشود، از بررسی خارج می‌شوند. این پیش‌فرض یک ترفند سازگاری با عقب است، تا کدی که هنوز از doctest.master.summarize همراه با testmod() استفاده می‌کند، همچنان برای اشیای بدون آزمون خروجی بگیرد. آرگومان exclude_empty در سازنده‌ی جدیدتر DocTestFinder به‌طور پیش‌فرض true است.

آرگومان‌های اختیاری extraglobs، verbose، report، optionflags، raise_on_error و globs همان آرگومان‌های تابع testfile() در بالا هستند، با این تفاوت که مقدار پیش‌فرض globs برابر با m.__dict__ است.

doctest.run_docstring_examples(f, globs, verbose=False, name='NoName', compileflags=None, optionflags=0)

مثال‌های مرتبط با شیء f را آزمون کنید؛ برای مثال، f می‌تواند یک رشته، یک ماژول، یک تابع یا یک شیء کلاس باشد.

برای زمینه اجرا از یک کپی سطحی از آرگومان دیکشنری globs استفاده می‌شود.

آرگومان اختیاری name در پیام‌های شکست استفاده می‌شود و مقدار پیش‌فرض آن "NoName" است.

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

آرگومان اختیاری compileflags مجموعه پرچم‌هایی را مشخص می‌کند که کامپایلر پایتون باید هنگام اجرای مثال‌ها از آن‌ها استفاده کند. به‌طور پیش‌فرض، یا اگر None باشد، پرچم‌ها متناظر با مجموعه ویژگی‌های آینده یافت‌شده در globs استنتاج می‌شوند.

آرگومان اختیاری optionflags همانند تابع testfile() در بالا عمل می‌کند.

API آزمون واحد

با رشد مجموعه ماژول‌های دارای doctest، به روشی برای اجرای سیستماتیک تمام doctestهای آن‌ها نیاز خواهید داشت. doctest دو تابع ارائه می‌دهد که می‌توان از آن‌ها برای ایجاد مجموعه‌های آزمون unittest از ماژول‌ها و پرونده‌های متنی حاوی doctest استفاده کرد. برای یکپارچه‌سازی با کشف آزمون unittest، یک تابع load_tests در ماژول آزمون خود قرار دهید:

import unittest
import doctest
import my_module_with_doctests

def load_tests(loader, tests, ignore):
    tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
    return tests

دو تابع اصلی برای ایجاد نمونه‌های unittest.TestSuite از پرونده‌های متنی و ماژول‌های دارای doctest وجود دارد:

doctest.DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)

تبدیل آزمون‌های doctest از یک یا چند پرونده‌ی متنی به یک unittest.TestSuite.

unittest.TestSuite بازگشتی برای اجرا توسط چارچوب unittest در نظر گرفته شده است و مثال‌های تعاملی در هر پرونده را اجرا می‌کند. اگر مثالی در هر پرونده‌ای شکست بخورد، آزمون واحد ساخته‌شده شکست می‌خورد و استثنای failureException پرتاب می‌شود که نام پرونده حاوی آزمون و شماره سطری (گاهی تقریبی) را نشان می‌دهد. اگر تمام مثال‌های یک پرونده رد شده باشند، آزمون واحد ساخته‌شده نیز به‌عنوان ردشده علامت‌گذاری می‌شود.

یک یا چند مسیر (به‌صورت رشته) برای پرونده‌های متنی که باید بررسی شوند، ارائه دهید.

می‌توان گزینه‌ها را به‌عنوان آرگومان‌های کلیدواژه‌ای ارائه کرد:

آرگومان اختیاری module_relative مشخص می‌کند که نام پرونده‌ها در paths چگونه باید تفسیر شوند:

  • اگر module_relative برابر True باشد (پیش‌فرض)، آنگاه هر نام پرونده در paths مسیری نسبی به ماژول و مستقل از سیستم‌عامل را مشخص می‌کند. به‌طور پیش‌فرض، این مسیر نسبت به پوشه‌ی ماژول فراخوان نسبی است؛ اما اگر آرگومان package مشخص شده باشد، نسبت به آن بسته نسبی است. برای اطمینان از استقلال از سیستم‌عامل، هر نام پرونده باید از نویسه‌های / برای جداسازی بخش‌های مسیر استفاده کند و نباید یک مسیر مطلق باشد (یعنی نباید با / شروع شود).

  • اگر module_relative برابر False باشد، هر نام پرونده در paths یک مسیر خاصِ سیستم‌عامل را مشخص می‌کند. مسیر می‌تواند مطلق یا نسبی باشد؛ مسیرهای نسبی با توجه به پوشه کاری جاری حل می‌شوند.

آرگومان اختیاری package یک بسته پایتون یا نام یک بسته پایتون است که پوشه آن باید به‌عنوان پوشه پایه برای نام پرونده‌های نسبت به ماژول در paths استفاده شود. اگر بسته‌ای مشخص نشده باشد، پوشه ماژول فراخوان به‌عنوان پوشه پایه برای نام پرونده‌های نسبت به ماژول استفاده می‌شود. اگر module_relative برابر False باشد، مشخص کردن package خطا است.

آرگومان اختیاری setUp یک تابع راه‌اندازی برای بدنه آزمون مشخص می‌کند. این تابع پیش از اجرای آزمون‌ها در هر پرونده فراخوانی می‌شود. یک شیء DocTest به تابع setUp ارسال می‌شود. تابع setUp می‌تواند به متغیرهای سراسری آزمون به‌عنوان ویژگی globs شیء آزمون ارسال‌شده دسترسی داشته باشد.

آرگومان اختیاری tearDown یک تابع پاک‌سازی (tear-down) برای بدنه آزمون مشخص می‌کند. این تابع پس از اجرای آزمون‌ها در هر پرونده فراخوانی می‌شود. یک شیء DocTest به تابع tearDown گذرانده خواهد شد. تابع tearDown می‌تواند به متغیرهای سراسری آزمون به‌عنوان ویژگی globs شیء آزمون گذرانده‌شده دسترسی داشته باشد.

آرگومان اختیاری globs یک دیکشنری حاوی متغیرهای سراسری اولیه برای آزمون‌ها است. یک رونوشت جدید از این دیکشنری برای هر آزمون ایجاد می‌شود. به‌طور پیش‌فرض، globs یک دیکشنری خالی جدید است.

آرگومان اختیاری optionflags، که با OR کردن پرچم‌های گزینه‌ی جداگانه با یکدیگر ایجاد می‌شود، گزینه‌های پیش‌فرض doctest برای آزمون‌ها را مشخص می‌کند. بخش پرچم‌های گزینه را ببینید. برای روش بهتر تنظیم گزینه‌های گزارش‌دهی، تابع set_unittest_reportflags() را در زیر ببینید.

آرگومان اختیاری parser، یک DocTestParser (یا زیرکلاس) را مشخص می‌کند که باید برای استخراج آزمون‌ها از پرونده‌ها استفاده شود. مقدار پیش‌فرض آن یک پارسر معمولی است (یعنی DocTestParser()).

آرگومان اختیاری encoding، کدگذاری‌ای را که باید برای تبدیل پرونده به یونیکد استفاده شود، مشخص می‌کند.

متغیر سراسری __file__ به متغیرهای سراسری ارائه‌شده به doctestهایی که از یک پرونده متنی با استفاده از DocFileSuite() بارگذاری می‌شوند، افزوده می‌شود.

doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, optionflags=0, checker=None)

تبدیل آزمون‌های doctest یک ماژول به unittest.TestSuite.

unittest.TestSuite برگردانده‌شده برای اجرا توسط چارچوب unittest در نظر گرفته شده است و هر doctest را در ماژول اجرا می‌کند. هر رشته مستند به‌عنوان یک آزمون واحد جداگانه اجرا می‌شود. اگر هر یک از doctestها شکست بخورد، آزمون واحد ساخته‌شده شکست می‌خورد و یک استثنای unittest.TestCase.failureException پرتاب می‌شود که نام پرونده حاوی آزمون و شماره سطری (گاهی تقریبی) را نشان می‌دهد. اگر همه‌ی مثال‌های یک رشته مستند رد شوند، آنگاه

آرگومان اختیاری module ماژول مورد آزمایش را فراهم می‌کند. این آرگومان می‌تواند یک شیء ماژول یا یک نام ماژول (که ممکن است نقطه‌دار باشد) باشد. اگر مشخص نشده باشد، از ماژول فراخواننده این تابع استفاده می‌شود.

آرگومان اختیاری globs یک دیکشنری حاوی متغیرهای سراسری اولیه برای آزمون‌ها است. برای هر آزمون، یک رونوشت جدید از این دیکشنری ایجاد می‌شود. به‌طور پیش‌فرض، globs همان __dict__ ماژول است.

آرگومان اختیاری extraglobs مجموعه‌ای اضافی از متغیرهای سراسری را مشخص می‌کند که در globs ادغام می‌شود. به‌طور پیش‌فرض، از هیچ متغیر سراسری اضافی استفاده نمی‌شود.

آرگومان اختیاری test_finder، شیء DocTestFinder (یا یک جایگزین مستقیم) است که برای استخراج doctestها از ماژول استفاده می‌شود.

آرگومان‌های اختیاری setUp، tearDown و optionflags همانند تابع DocFileSuite() در بالا هستند، اما برای هر رشته مستند فراخوانی می‌شوند.

این تابع از همان روش جست‌وجوی testmod() استفاده می‌کند.

تغییر یافته در نسخه‌ی 3.5: DocTestSuite() اگر module حاوی هیچ رشته‌مستندی نباشد، به‌جای پرتاب ValueError، یک unittest.TestSuite خالی را برمی‌گرداند.

در پشت صحنه، DocTestSuite() از نمونه‌های doctest.DocTestCase یک unittest.TestSuite می‌سازد، و DocTestCase زیرکلاسی از unittest.TestCase است. DocTestCase در اینجا مستند نشده است (این جزئیاتی داخلی است)، اما بررسی کد آن می‌تواند به پرسش‌هایی درباره‌ی جزئیات دقیق ادغام unittest پاسخ دهد.

به‌طور مشابه، DocFileSuite() یک unittest.TestSuite از نمونه‌های doctest.DocFileCase ایجاد می‌کند، و DocFileCase زیرکلاسی از DocTestCase است.

بنابراین هر دو روش ایجاد یک unittest.TestSuite نمونه‌های DocTestCase را اجرا می‌کنند. این موضوع به یک دلیل ظریف مهم است: وقتی خودتان توابع doctest را اجرا می‌کنید، می‌توانید گزینه‌های doctest در حال استفاده را مستقیماً با ارسال پرچم‌های گزینه به توابع doctest کنترل کنید. با این حال، اگر در حال نوشتن یک چارچوب unittest هستید، unittest در نهایت کنترل می‌کند که آزمون‌ها چه زمانی و چگونه اجرا شوند. نویسنده چارچوب معمولاً می‌خواهد گزینه‌های گزارش‌دهی doctest را کنترل کند (شاید، مثلاً، مشخص‌شده با گزینه‌های خط فرمان)، اما راهی برای ارسال گزینه‌ها از طریق unittest به اجراکننده‌های آزمون doctest وجود ندارد.

به همین دلیل، doctest همچنین از مفهومی از پرچم‌های گزارش‌دهی doctest که مخصوص پشتیبانی از unittest است، از طریق این تابع پشتیبانی می‌کند:

doctest.set_unittest_reportflags(flags)

پرچم‌های گزارش‌دهی doctest را برای استفاده تنظیم کنید.

آرگومان flags OR بیتی پرچم‌های گزینه را می‌گیرد. بخش پرچم‌های گزینه را ببینید. فقط می‌توان از «پرچم‌های گزارش‌دهی» استفاده کرد.

این یک تنظیم سراسری ماژول است و بر تمام دوک‌آزمون‌های آینده (doctest) که توسط ماژول unittest اجرا می‌شوند، تأثیر می‌گذارد: متد runTest() کلاس DocTestCase پرچم‌های گزینه را که هنگام ساخت نمونه‌ی DocTestCase برای مورد آزمون مشخص‌شده‌اند، بررسی می‌کند. اگر پرچم‌های گزارش‌دهی مشخص‌نشده باشند (که حالت معمول و مورد انتظار است)، پرچم‌های گزارش‌دهی unittest در doctest با پرچم‌های گزینه به‌صورت OR بیتی ترکیب می‌شوند، و پرچم‌های گزینه‌ای که بدین‌ترتیب افزایش‌یافته‌اند، به نمونه‌ی DocTestRunner ساخته‌شده برای اجرای دوک‌آزمون داده می‌شوند. اگر هنگام ساخت نمونه‌ی DocTestCase پرچم‌های گزارش‌دهی مشخص‌شده باشند، پرچم‌های گزارش‌دهی unittest در doctest نادیده گرفته می‌شوند.

مقدار پرچم‌های گزارش‌دهی unittest که پیش از فراخوانی تابع فعال بودند، توسط تابع بازگردانده می‌شود.

API پیشرفته

API پایه پوششی ساده است که برای آسان کردن استفاده از doctest در نظر گرفته شده است. این API نسبتاً انعطاف‌پذیر است و باید نیازهای بیشتر کاربران را برآورده کند؛ با این حال، اگر به کنترل ریزدانه‌تری بر آزمون نیاز دارید یا می‌خواهید قابلیت‌های doctest را گسترش دهید، باید از API پیشرفته استفاده کنید.

API پیشرفته حول دو کلاس ظرف می‌چرخد که برای ذخیره‌سازی مثال‌های تعاملی استخراج‌شده از موارد doctest به‌کار می‌روند:

  • Example: یک دستور پایتون، همراه با خروجی مورد انتظار آن.

  • DocTest: مجموعه‌ای از نمونه‌های Example، که معمولاً از یک رشته مستند یا پرونده متنی واحد استخراج می‌شود.

کلاس‌های پردازش دیگری برای یافتن، تجزیه، اجرا و بررسی نمونه‌های doctest تعریف شده‌اند:

  • DocTestFinder: همه‌ی رشته مستندات را در یک ماژول داده‌شده پیدا می‌کند و از یک DocTestParser برای ایجاد یک DocTest از هر رشته مستندسازی که شامل مثال‌های تعاملی باشد، استفاده می‌کند.

  • DocTestParser: یک شیء DocTest را از یک رشته ایجاد می‌کند (مانند رشته‌ی مستندسازی یک شیء).

  • DocTestRunner: مثال‌های موجود در یک DocTest را اجرا می‌کند و برای صحت‌سنجی خروجی آن‌ها از یک OutputChecker استفاده می‌کند.

  • OutputChecker: خروجی واقعی یک مثال doctest را با خروجی مورد انتظار مقایسه می‌کند و تصمیم می‌گیرد که آیا این دو با هم مطابقت دارند یا خیر.

روابط میان این کلاس‌های پردازشی در نمودار زیر خلاصه شده‌اند:

                            فهرستی از:
+------+                   +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> نتایج
+------+    |        ^     +---------+     |       ^    (چاپ شده)
            |        |     | Example |     |       |
            v        |     |   ...   |     v       |
           DocTestParser   | Example |   OutputChecker
                           +---------+

اشیای DocTest

class doctest.DocTest(examples, globs, name, filename, lineno, docstring)

مجموعه‌ای از مثال‌های doctest که باید در یک فضای نام واحد اجرا شوند. آرگومان‌های سازنده برای مقداردهی اولیه ویژگی‌هایی با همان نام‌ها استفاده می‌شوند.

DocTest ویژگی‌های زیر را تعریف می‌کند. این ویژگی‌ها توسط سازنده مقداردهی اولیه می‌شوند و نباید به‌طور مستقیم تغییر داده شوند.

examples

فهرستی از اشیای Example که نمونه‌های تعاملی پایتونِ جداگانه‌ای را، که باید توسط این آزمون اجرا شوند، کدگذاری می‌کنند.

globs

فضای نامی (که به آن globals نیز گفته می‌شود) که مثال‌ها باید در آن اجرا شوند. این یک دیکشنری است که نام‌ها را به مقدارها نگاشت می‌کند. هر تغییری در فضای نام که توسط مثال‌ها انجام شود (مانند ایجاد متغیرهای جدید) پس از اجرای آزمون در globs منعکس خواهد شد.

name

یک نام رشته‌ای که DocTest را شناسایی می‌کند. معمولاً این نام، نام شیء یا پرونده‌ای است که آزمون از آن استخراج شده است.

filename

نام پرونده‌ای که این DocTest از آن استخراج شده است؛ یا None اگر نام پرونده معلوم نباشد، یا اگر DocTest از پرونده‌ای استخراج نشده باشد.

lineno

شماره خط در filename که این DocTest در آن آغاز می‌شود، یا None اگر شماره خط در دسترس نیست. این شماره خط نسبت به ابتدای پرونده صفرپایه است.

docstring

رشته‌ای که آزمون از آن استخراج شده است، یا None اگر رشته در دسترس نباشد، یا اگر آزمون از رشته‌ای استخراج نشده باشد.

اشیای Example

class doctest.Example(source, want, exc_msg=None, lineno=0, indent=0, options=None)

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

Example ویژگی‌های زیر را تعریف می‌کند. این ویژگی‌ها توسط سازنده مقداردهی اولیه می‌شوند و نباید به‌طور مستقیم تغییر داده شوند.

source

رشته‌ای حاوی کد منبع مثال. این کد منبع شامل یک دستور پایتون است و همیشه با یک خط جدید پایان می‌یابد؛ سازنده در صورت لزوم یک خط جدید اضافه می‌کند.

want

خروجی مورد انتظار از اجرای کد منبع مثال (یا از stdout، یا ردگیری پشته در صورت بروز استثنا). want با یک نویسه خط جدید پایان می‌یابد، مگر اینکه هیچ خروجی‌ای مورد انتظار نباشد، که در این صورت یک رشته خالی است. سازنده در صورت لزوم یک نویسه خط جدید اضافه می‌کند.

exc_msg

پیام استثنای تولیدشده توسط مثال، اگر انتظار می‌رود که مثال یک استثنا تولید کند؛ یا None اگر انتظار نمی‌رود که استثنا تولید کند. این پیام استثنا با مقدار بازگشتی traceback.format_exception_only() مقایسه می‌شود. exc_msg به یک خط جدید ختم می‌شود، مگر اینکه None باشد. سازنده در صورت نیاز یک خط جدید اضافه می‌کند.

lineno

شماره خط در رشته حاوی این مثال که مثال در آن آغاز می‌شود. این شماره خط نسبت به ابتدای رشته حاوی، صفرمبنا است.

indent

تورفتگی مثال در رشته‌ی حاوی آن، یعنی تعداد نویسه‌های فاصله‌ای که پیش از اولین اعلان مثال قرار دارند.

options

دیکشنری‌ای که نگاشتی از پرچم‌های گزینه به True یا False است و برای بازنویسی گزینه‌های پیش‌فرض این مثال استفاده می‌شود. پرچم‌های گزینه‌ای که در این دیکشنری وجود نداشته باشند، روی مقدار پیش‌فرض خود باقی می‌مانند (طبق optionflags DocTestRunner). به‌طور پیش‌فرض، هیچ گزینه‌ای تنظیم نشده است.

اشیای DocTestFinder

class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)

یک کلاس پردازشی که برای استخراج DocTestهای مرتبط با یک شیء داده‌شده، از رشته‌ی مستندسازی آن و رشته مستندات اشیاء درون آن استفاده می‌شود. DocTest ها را می‌توان از ماژول‌ها، کلاس‌ها، توابع، متدها، متدهای ایستا (staticmethod)، متدهای کلاس (classmethod) و پراپرتی‌ها استخراج کرد.

از آرگومان اختیاری verbose می‌توان برای نمایش اشیای جستجوشده توسط یابنده استفاده کرد. مقدار پیش‌فرض آن False است (بدون خروجی).

آرگومان اختیاری parser، شیء DocTestParser (یا یک جایگزین مستقیم (drop-in replacement)) را که برای استخراج doctestها از docstringها به کار می‌رود، تعیین می‌کند.

اگر آرگومان اختیاری recurse نادرست باشد، آنگاه DocTestFinder.find() فقط شیء داده‌شده را بررسی می‌کند و هیچ‌یک از اشیای درون آن را بررسی نمی‌کند.

اگر آرگومان اختیاری exclude_empty نادرست باشد، DocTestFinder.find() شامل آزمون‌هایی برای اشیایی با رشته مستندات‌های خالی خواهد شد.

DocTestFinder متد زیر را تعریف می‌کند:

find(obj[, name][, module][, globs][, extraglobs])

فهرستی از DocTests را برمی‌گرداند که توسط رشته‌ی مستندسازی شیء obj، یا توسط هر یک از رشته مستندات اشیاء درون آن تعریف شده‌اند.

آرگومان اختیاری name نام شیء را مشخص می‌کند؛ این نام برای ساخت نام‌هایی برای DocTests بازگشتی استفاده خواهد شد. اگر name مشخص نشده باشد، از obj.__name__ استفاده می‌شود.

پارامتر اختیاری module ماژولی است که شیء داده‌شده را در بر دارد. اگر ماژول مشخص نشده باشد یا None باشد، یافت‌کننده آزمون تلاش می‌کند تا ماژول صحیح را به‌طور خودکار تعیین کند. ماژول شیء مورد استفاده قرار می‌گیرد:

  • به‌عنوان یک فضای نام پیش‌فرض، اگر globs مشخص نشده باشد.

  • برای جلوگیری از استخراج DocTests از اشیایی که از ماژول‌های دیگر ایمپورت شده‌اند. (اشیای دربرگرفته‌شده‌ای که ماژول آن‌ها غیر از module است، نادیده گرفته می‌شوند.)

  • برای یافتن نام پرونده‌ای که حاوی شیء است.

  • برای کمک به یافتن شماره‌ی خط شیء در پرونده آن.

اگر module برابر False باشد، هیچ تلاشی برای یافتن ماژول انجام نخواهد شد. این حالت مبهم است و بیشتر در آزمایش خود doctest کاربرد دارد: اگر module برابر False باشد، یا None باشد ولی به‌طور خودکار یافت نشود، آنگاه تمام اشیاء متعلق به ماژول (ناموجود) در نظر گرفته می‌شوند، بنابراین تمام اشیاء داخلی به‌صورت بازگشتی برای یافتن doctestها جستجو خواهند شد.

متغیرهای سراسری هر DocTest با ترکیب globs و extraglobs تشکیل می‌شود (انتساب‌های موجود در extraglobs جایگزین انتساب‌های موجود در globs می‌شوند). برای هر DocTest یک کپی کم‌عمق جدید از دیکشنری متغیرهای سراسری ایجاد می‌شود. اگر globs مشخص‌نشده باشد، مقدار پیش‌فرض آن __dict__ ماژول، در صورت مشخص بودن ماژول، و در غیر این صورت {} خواهد بود. اگر extraglobs مشخص‌نشده باشد، مقدار پیش‌فرض آن {} است.

اشیای DocTestParser

class doctest.DocTestParser

یک کلاس پردازشی که برای استخراج مثال‌های تعاملی از یک رشته و استفاده از آن‌ها برای ایجاد یک شیء DocTest به کار می‌رود.

DocTestParser متدهای زیر را تعریف می‌کند:

get_doctest(string, globs, name, filename, lineno)

تمام مثال‌های doctest را از رشته‌ی داده‌شده استخراج می‌کند و آن‌ها را در یک شیء DocTest جمع‌آوری می‌کند.

globs، name، filename و lineno ویژگی‌های شیء جدید DocTest هستند. برای اطلاعات بیشتر، مستندات DocTest را ببینید.

get_examples(string, name='<string>')

تمام مثال‌های doctest را از رشته داده‌شده استخراج می‌کند و آن‌ها را به‌صورت فهرستی از اشیای Example برمی‌گرداند. شماره سطرها از ۰ شروع می‌شود. آرگومان اختیاری name نامی برای شناسایی این رشته است و فقط در پیام‌های خطا استفاده می‌شود.

parse(string, name='<string>')

رشته داده‌شده را به مثال‌ها و متن‌های میان آن‌ها تقسیم می‌کند و آن‌ها را به‌صورت فهرستی متناوب از Example ها و رشته‌ها برمی‌گرداند. شماره‌ی سطرهای برای Example ها مبنای ۰ است. آرگومان اختیاری name نامی برای شناسایی این رشته است و فقط برای پیام‌های خطا استفاده می‌شود.

اشیاء TestResults

class doctest.TestResults(failed, attempted)
failed

تعداد آزمون‌های ناموفق.

attempted

تعداد آزمون‌های انجام‌شده.

skipped

تعداد آزمون‌های ردشده.

اضافه شده در نسخه‌ی 3.13.

اشیای DocTestRunner

class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0)

یک کلاس پردازشی که برای اجرا و صحت‌سنجی مثال‌های تعاملی در یک DocTest استفاده می‌شود.

مقایسه بین خروجی‌های مورد انتظار و خروجی‌های واقعی توسط یک OutputChecker انجام می‌شود. این مقایسه می‌تواند با تعدادی پرچم گزینه سفارشی‌سازی شود؛ برای اطلاعات بیشتر بخش پرچم‌های گزینه را ببینید. اگر پرچم‌های گزینه کافی نباشند، این مقایسه همچنین می‌تواند با ارسال یک زیرکلاس از OutputChecker به سازنده سفارشی‌سازی شود.

خروجی نمایشی اجراکننده‌ی آزمون را می‌توان به دو روش کنترل کرد. نخست، می‌توان یک تابع خروجی به run() ارسال کرد؛ این تابع با رشته‌هایی که باید نمایش داده شوند فراخوانی می‌شود. مقدار پیش‌فرض آن sys.stdout.write است. اگر گرفتن خروجی کافی نباشد، می‌توان خروجی نمایشی را نیز با ایجاد زیرکلاس از DocTestRunner و بازنویسی متدهای report_start()، report_success()، report_unexpected_exception() و report_failure() سفارشی‌سازی کرد.

آرگومان کلیدواژه‌ای اختیاری checker، شیء OutputChecker (یا جایگزین مستقیم (drop-in replacement)) را مشخص می‌کند که باید برای مقایسه‌ی خروجی‌های مورد انتظار با خروجی‌های واقعی مثال‌های doctest استفاده شود.

آرگومان کلیدواژه‌ای اختیاری verbose میزان پرگویی DocTestRunner را کنترل می‌کند. اگر verbose برابر True باشد، اطلاعات مربوط به هر مثال هنگام اجرای آن چاپ می‌شود. اگر verbose برابر False باشد، فقط شکست‌ها چاپ می‌شوند. اگر verbose مشخص‌نشده باشد یا None باشد، خروجی پرگو فقط و فقط در صورتی به کار می‌رود که از سوییچ خط فرمان -v استفاده شود.

می‌توان از آرگومان کلیدواژه‌ای اختیاری optionflags برای کنترل نحوه‌ی مقایسه‌ی خروجی مورد انتظار با خروجی واقعی توسط اجراکننده‌ی آزمون و نحوه‌ی نمایش شکست‌ها استفاده کرد. برای اطلاعات بیشتر، بخش پرچم‌های گزینه را ببینید.

اجراکننده آزمون آمار را گردآوری می‌کند. تعداد تجمیعی مثال‌های اجراشده، ناموفق و ردشده نیز از طریق ویژگی‌های tries، failures و skips در دسترس است. متدهای run() و summarize() یک نمونه از TestResults را برمی‌گردانند.

DocTestRunner متدهای زیر را تعریف می‌کند:

report_start(out, test, example)

گزارش می‌دهد که اجراکننده‌ی آزمون در آستانه‌ی پردازش مثال داده‌شده است. این متد ارائه شده است تا زیرکلاس‌های DocTestRunner بتوانند خروجی خود را سفارشی‌سازی کنند؛ نباید مستقیماً فراخوانی شود.

example مثالی است که در آستانه‌ی پردازش قرار دارد. test آزمونی است که شامل example می‌شود. out تابع خروجی است که به DocTestRunner.run() پاس داده شده است.

report_success(out, test, example, got)

گزارش می‌دهد که مثال داده‌شده با موفقیت اجرا شده است. این متد ارائه شده است تا زیرکلاس‌های DocTestRunner بتوانند خروجی خود را سفارشی‌سازی کنند؛ نباید مستقیماً فراخوانی شود.

example مثالی است که در شرف پردازش قرار دارد. got خروجی واقعی مثال است. test آزمونی است که example را شامل می‌شود. out تابع خروجی‌ای است که به DocTestRunner.run() ارسال شده است.

report_failure(out, test, example, got)

گزارش می‌دهد که مثال داده‌شده شکست خورده است. این متد ارائه شده است تا زیرکلاس‌های DocTestRunner بتوانند خروجی خود را سفارشی کنند؛ نباید به‌صورت مستقیم فراخوانی شود.

example مثالی است که در شرف پردازش قرار دارد. got خروجی واقعی مثال است. test آزمونی است که example را شامل می‌شود. out تابع خروجی‌ای است که به DocTestRunner.run() ارسال شده است.

report_unexpected_exception(out, test, example, exc_info)

گزارش کن که مثال داده‌شده استثنای غیرمنتظره‌ای را پرتاب کرده است. این متد ارائه شده است تا زیرکلاس‌های DocTestRunner بتوانند خروجی خود را سفارشی‌سازی کنند؛ نباید مستقیماً فراخوانی شود.

example مثالی است که در آستانه‌ی پردازش است. exc_info تاپلی شامل اطلاعات مربوط به استثنای غیرمنتظره است (همان‌طور که توسط sys.exc_info() برگردانده می‌شود). test آزمونی است که شامل example است. out تابع خروجی‌ای است که به DocTestRunner.run() پاس داده شده است.

run(test, compileflags=None, out=None, clear_globs=True)

مثال‌های موجود در test (یک شیء DocTest) را اجرا می‌کند و نتایج را با استفاده از تابع نویسنده out نمایش می‌دهد. یک نمونه TestResults را برمی‌گرداند.

مثال‌ها در فضای نام test.globs اجرا می‌شوند. اگر clear_globs درست باشد (پیش‌فرض)، این فضای نام پس از اجرای آزمون پاک‌سازی می‌شود تا به زباله‌روبی کمک کند. اگر می‌خواهید پس از پایان آزمون فضای نام را بررسی کنید، از clear_globs=False استفاده کنید.

compileflags مجموعه‌ای از پرچم‌ها را مشخص می‌کند که باید هنگام اجرای مثال‌ها توسط کامپایلر پایتون استفاده شوند. اگر مشخص نشده باشد، به‌طور پیش‌فرض مجموعه‌ای از پرچم‌های future-import خواهد بود که بر globs اعمال می‌شوند.

خروجی هر مثال با استفاده از بررسی‌کننده خروجی DocTestRunner بررسی می‌شود، و نتایج توسط متدهای DocTestRunner.report_*() قالب‌بندی می‌شوند.

summarize(verbose=None)

خلاصه‌ای از تمام موارد آزمونی که توسط این DocTestRunner اجرا شده‌اند را چاپ می‌کند و نمونه‌ای از TestResults را بازمی‌گرداند.

آرگومان اختیاری verbose میزان جزئیات خلاصه را کنترل می‌کند. اگر میزان جزئیات مشخص نشده باشد، از میزان جزئیات DocTestRunner استفاده می‌شود.

DocTestParser ویژگی‌های زیر را دارد:

tries

تعداد مثال‌های آزمایش‌شده.

failures

تعداد مثال‌های ناموفق.

skips

تعداد مثال‌های ردشده.

اضافه شده در نسخه‌ی 3.13.

اشیای OutputChecker

class doctest.OutputChecker

کلاسی که برای بررسی تطابق خروجی واقعی یک مثال doctest با خروجی مورد انتظار استفاده می‌شود. OutputChecker دو متد تعریف می‌کند: check_output()، که یک جفت خروجی داده‌شده را مقایسه می‌کند و در صورت مطابقت آن‌ها True را برمی‌گرداند؛ و output_difference()، که رشته‌ای را برمی‌گرداند که تفاوت‌های بین دو خروجی را توصیف می‌کند.

OutputChecker متدهای زیر را تعریف می‌کند:

check_output(want, got, optionflags)

اگر و تنها اگر خروجی واقعی یک مثال (got) با خروجی مورد انتظار (want) مطابقت داشته باشد، True را برمی‌گرداند. این رشته‌ها همیشه در صورتی که یکسان باشند، منطبق در نظر گرفته می‌شوند؛ اما بسته به پرچم‌های گزینه‌ای که اجراکننده‌ی آزمون از آن‌ها استفاده می‌کند، چندین نوع انطباق غیردقیق نیز امکان‌پذیر است. برای اطلاعات بیشتر درباره‌ی پرچم‌های گزینه، بخش پرچم‌های گزینه را ببینید.

output_difference(example, got, optionflags)

رشته‌ای برمی‌گرداند که تفاوت‌های بین خروجی مورد انتظار برای یک مثال داده‌شده (example) و خروجی واقعی (got) را توصیف می‌کند. optionflags مجموعه‌ای از پرچم‌های گزینه است که برای مقایسه‌ی want و got به کار می‌رود.

اشکال‌زدایی

Doctest چندین سازوکار برای اشکال‌زدایی مثال‌های doctest فراهم می‌کند:

  • چندین تابع doctestها را به برنامه‌های قابل‌اجرای پایتون تبدیل می‌کنند که می‌توان آن‌ها را تحت اشکال‌زدای پایتون، pdb اجرا کرد.

  • کلاس DebugRunner یک زیرکلاس از DocTestRunner است که برای اولین مثال ناموفق، استثنایی پرتاب می‌کند که حاوی اطلاعاتی درباره آن مثال است. می‌توان از این اطلاعات برای انجام اشکال‌زدایی پس از وقوع خطا (post-mortem debugging) روی مثال استفاده کرد.

  • موارد آزمون unittest که توسط DocTestSuite() تولید شده‌اند، از متد debug() تعریف‌شده در unittest.TestCase پشتیبانی می‌کنند.

  • می‌توانید یک فراخوانی به pdb.set_trace() را در یک مثال doctest اضافه کنید، و هنگامی که آن خط اجرا می‌شود، وارد اشکال‌زدای پایتون می‌شوید. سپس می‌توانید مقادیر فعلی متغیرها و غیره را بررسی کنید. برای مثال، فرض کنید a.py فقط شامل این رشته‌مستندات ماژول است:

    >>> def f(x):
    ...     g(x*2)
    >>> def g(x):
    ...     print(x+3)
    ...     import pdb; pdb.set_trace()
    >>> f(3)
    9
    

    سپس یک نشست تعاملی پایتون ممکن است به این شکل باشد:

    >>> import a, doctest
    >>> doctest.testmod(a)
    --Return--
    > <doctest a[1]>(3)g()->None
    -> import pdb; pdb.set_trace()
    (Pdb) list
      1     def g(x):
      2         print(x+3)
      3  ->     import pdb; pdb.set_trace()
    [EOF]
    (Pdb) p x
    6
    (Pdb) step
    --Return--
    > <doctest a[0]>(2)f()->None
    -> g(x*2)
    (Pdb) list
      1     def f(x):
      2  ->     g(x*2)
    [EOF]
    (Pdb) p x
    3
    (Pdb) step
    --Return--
    > <doctest a[2]>(1)?()->None
    -> f(3)
    (Pdb) cont
    (0, 3)
    >>>
    

توابعی که doctestها را به کد پایتون تبدیل می‌کنند، و احتمالاً کد ساخته‌شده را زیر نظر اشکال‌زدا اجرا می‌کنند:

doctest.script_from_examples(s)

متن همراه با مثال‌ها را به یک اسکریپت تبدیل کنید.

آرگومان s رشته‌ای حاوی مثال‌های doctest است. این رشته به یک اسکریپت پایتون تبدیل می‌شود، به‌طوری که مثال‌های doctest درون s به کد معمولی تبدیل می‌شوند و هر چیز دیگر به کامنت‌های پایتون تبدیل می‌شود. اسکریپت تولیدشده به‌صورت یک رشته برگردانده می‌شود. برای مثال،

import doctest
print(doctest.script_from_examples(r"""
    Set x and y to 1 and 2.
    >>> x, y = 1, 2

    Print their sum:
    >>> print(x+y)
    3
"""))

نمایش می‌دهد:

# Set x and y to 1 and 2.
x, y = 1, 2
#
# Print their sum:
print(x+y)
# Expected:
## 3

این تابع به‌صورت داخلی توسط توابع دیگر استفاده می‌شود (در زیر ببینید)، اما می‌تواند هنگامی که بخواهید یک نشست تعاملی پایتون را به یک اسکریپت پایتون (Python script) تبدیل کنید نیز مفید باشد.

doctest.testsource(module, name)

doctest یک شیء را به یک اسکریپت تبدیل کنید.

آرگومان module یک شیء ماژول، یا نام نقطه‌دار یک ماژول است که شامل شیءای است که آزمون‌های مستند (doctests) آن مورد نظر هستند. آرگومان name نام شیء (درون ماژول) با آزمون‌های مستند (doctests) مورد نظر است. نتیجه یک رشته است که شامل رشته مستند شیء است که به یک اسکریپت پایتون تبدیل شده است، همان‌طور که برای script_from_examples() در بالا توضیح داده شد. برای مثال، اگر ماژول a.py شامل یک تابع سطح بالا f() باشد، آنگاه

import a, doctest
print(doctest.testsource(a, "a.f"))

نسخه‌ی اسکریپتی رشته‌مستند تابع f() را چاپ می‌کند، به‌طوری که doctestها به کد تبدیل شده‌اند و بقیه در کامنت‌ها قرار گرفته‌اند.

doctest.debug(module, name, pm=False)

doctestهای یک شیء را اشکال‌زدایی کنید.

آرگومان‌های module و name همان آرگومان‌های تابع testsource() در بالا هستند. اسکریپت پایتون ساخته‌شده برای رشته‌مستند شیء نام‌برده‌شده، در یک پرونده موقت نوشته می‌شود و سپس آن پرونده تحت کنترل اشکال‌زدای پایتون، pdb، اجرا می‌شود.

یک کپی سطحی از module.__dict__ برای هر دو زمینه اجرای محلی و سراسری استفاده می‌شود.

آرگومان اختیاری pm تعیین می‌کند که آیا از اشکال‌زدایی پس از خاتمه (post-mortem debugging) استفاده شود یا خیر. اگر pm مقدار درست داشته باشد، پرونده‌ی اسکریپت مستقیماً اجرا می‌شود و اشکال‌زدا تنها در صورتی وارد عمل می‌شود که اسکریپت با پرتاب یک استثنای مدیریت‌نشده خاتمه یابد. اگر چنین شود، اشکال‌زدایی پس از خاتمه از طریق pdb.post_mortem() و با ارسال شیء ردگیری از استثنای مدیریت‌نشده فراخوانی می‌شود. اگر pm مشخص نشده باشد یا مقدار نادرست داشته باشد، اسکریپت از همان ابتدا با ارسال یک فراخوانی مناسب از exec() به pdb.run()، زیر نظر اشکال‌زدا اجرا می‌شود.

doctest.debug_src(src, pm=False, globs=None)

doctestها را در یک رشته اشکال‌زدایی کنید.

این مانند تابع debug() در بالا است، با این تفاوت که یک رشته حاوی نمونه‌های doctest مستقیماً از طریق آرگومان src مشخص می‌شود.

آرگومان اختیاری pm همان معنایی را دارد که در تابع debug() در بالا آمده است.

آرگومان اختیاری globs یک دیکشنری را برای استفاده به‌عنوان زمینه‌ی اجرای محلی و سراسری فراهم می‌کند. اگر مشخص نشده باشد، یا None باشد، از یک دیکشنری خالی استفاده می‌شود. اگر مشخص شده باشد، از یک کپی کم‌عمق از دیکشنری استفاده می‌شود.

کلاس DebugRunner و استثناهای ویژه‌ای که ممکن است پرتاب کند، بیشتر مورد توجه نویسندگان چارچوب‌های آزمون هستند و در اینجا تنها به‌طور خلاصه شرح داده می‌شوند. برای جزئیات بیشتر، کد منبع و به‌ویژه رشته‌مستند DebugRunner (که یک doctest است!) را ببینید:

class doctest.DebugRunner(checker=None, verbose=None, optionflags=0)

زیرکلاسی از DocTestRunner که به محض مواجهه با یک شکست، یک استثنا پرتاب می‌کند. اگر یک استثنای غیرمنتظره رخ دهد، یک استثنای UnexpectedException پرتاب می‌شود که شامل آزمون، مثال و استثنای اصلی است. اگر خروجی مطابقت نداشته باشد، یک استثنای DocTestFailure پرتاب می‌شود که شامل آزمون، مثال و خروجی واقعی است.

برای اطلاعات در مورد پارامترهای سازنده و متدها، مستندات DocTestRunner را در بخش API پیشرفته ببینید.

دو استثنا وجود دارد که ممکن است توسط نمونه‌های DebugRunner پرتاب شوند:

exception doctest.DocTestFailure(test, example, got)

استثنایی که DocTestRunner آن را پرتاب می‌کند تا نشان دهد خروجی واقعی یک مثال doctest با خروجی مورد انتظار آن مطابقت ندارد. آرگومان‌های سازنده برای مقداردهی اولیه‌ی ویژگی‌هایی با همان نام‌ها استفاده می‌شوند.

DocTestFailure ویژگی‌های زیر را تعریف می‌کند:

DocTestFailure.test

شیء DocTest که در زمان شکست خوردن مثال در حال اجرا بود.

DocTestFailure.example

Example که شکست خورد.

DocTestFailure.got

خروجی واقعی مثال.

exception doctest.UnexpectedException(test, example, exc_info)

استثنایی که توسط DocTestRunner پرتاب می‌شود تا نشان دهد که یک مثال doctest استثنای غیرمنتظره‌ای را پرتاب کرده است. آرگومان‌های سازنده برای مقداردهی اولیه‌ی ویژگی‌هایی با همان نام‌ها استفاده می‌شوند.

UnexpectedException ویژگی‌های زیر را تعریف می‌کند:

UnexpectedException.test

شیء DocTest که در زمان شکست خوردن مثال در حال اجرا بود.

UnexpectedException.example

Example که شکست خورد.

UnexpectedException.exc_info

یک تاپل حاوی اطلاعات درباره استثنای غیرمنتظره، همان‌طور که توسط sys.exc_info() برگردانده می‌شود.

تریبون

همان‌طور که در مقدمه ذکر شد، doctest گسترش یافته است تا سه کاربرد اصلی داشته باشد:

  1. بررسی مثال‌ها در رشته مستندات.

  2. آزمون رگرسیون.

  3. مستندات قابل‌اجرا / آزمون‌نویسی ادبی.

این کاربردها نیازمندی‌های متفاوتی دارند و مهم است که میان آن‌ها تمایز قائل شوید. به‌ویژه، پر کردن رشته مستندات‌تان با موارد آزمون مبهم، باعث ایجاد مستندات بد می‌شود.

هنگام نوشتن یک رشته‌ی مستندسازی، مثال‌های آن را با دقت انتخاب کنید. این کار ظرافتی دارد که باید آموخته شود — ممکن است در ابتدا برایتان طبیعی نباشد. مثال‌ها باید ارزش واقعی به مستندات بیفزایند. یک مثال خوب اغلب می‌تواند ارزشی برابر با کلمات بسیاری داشته باشد. اگر با دقت تهیه شوند، مثال‌ها برای کاربران شما بی‌نهایت ارزشمند خواهند بود و با گذشت سال‌ها و تغییر شرایط، زمانی را که صرف گردآوری آن‌ها شده است، چندین برابر جبران خواهند کرد. هنوز هم از تعداد دفعاتی که یکی از مثال‌های doctest من پس از یک تغییر «بی‌خطر» از کار می‌افتد، شگفت‌زده می‌شوم.

Doctest همچنین ابزار عالی برای آزمون رگرسیون است، به‌ویژه اگر در متن توضیحی کم‌کاری نکنید. با درهم‌آمیختن متن و مثال‌ها، پیگیری اینکه چه چیزی واقعاً آزمون می‌شود و چرا، بسیار آسان‌تر می‌شود. هنگامی که یک آزمون شکست می‌خورد، متن خوب می‌تواند تشخیص مشکل و چگونگی اصلاح آن را بسیار آسان‌تر کند. درست است که می‌توانید کامنت‌های مفصلی در آزمون مبتنی بر کد بنویسید، اما برنامه‌نویسان کمی این کار را انجام می‌دهند. بسیاری دریافته‌اند که در عوض، استفاده از رویکردهای doctest به آزمون‌های بسیار واضح‌تری منجر می‌شود. شاید این صرفاً به این دلیل باشد که doctest نوشتن متن را کمی آسان‌تر از نوشتن کد می‌کند، در حالی که نوشتن کامنت در کد کمی سخت‌تر است. به نظر من، موضوع عمیق‌تر از این است: نگرش طبیعی هنگام نوشتن آزمون مبتنی بر doctest این است که می‌خواهید نکات دقیق نرم‌افزار خود را توضیح دهید و آن‌ها را با مثال‌ها نشان دهید. این امر به‌نوبه خود به‌طور طبیعی به پرونده‌های آزمون منجر می‌شود که با ساده‌ترین قابلیت‌ها شروع می‌شوند و به‌صورت منطقی به سمت پیچیدگی‌ها و حالت‌های مرزی پیش می‌روند. نتیجه یک روایت منسجم است، نه مجموعه‌ای از توابع جداگانه که بخش‌های جداگانه‌ای از قابلیت‌ها را گویا به‌صورت تصادفی آزمون می‌کنند. این نگرشی متفاوت است و نتایج متفاوتی به وجود می‌آورد و تمایز میان آزمون و توضیح را کم‌رنگ می‌کند.

بهتر است آزمون رگرسیون (regression testing) به اشیاء یا پرونده‌های اختصاصی محدود شود. گزینه‌های متعددی برای سازمان‌دهی آزمون‌ها وجود دارد:

  • پرونده‌های متنی حاوی موارد آزمون را به‌صورت مثال‌های تعاملی بنویسید و این پرونده‌ها را با استفاده از testfile() یا DocFileSuite() آزمایش کنید. این روش توصیه می‌شود، هرچند انجام آن برای پروژه‌های جدیدی که از ابتدا برای استفاده از doctest طراحی شده‌اند، آسان‌تر است.

  • توابعی به نام _regrtest_topic تعریف کنید که هر کدام تنها از یک رشته‌مستند حاوی موارد آزمون برای موضوعات نام‌برده تشکیل شده‌اند. این توابع می‌توانند در همان پرونده ماژول گنجانده شوند، یا در یک پرونده آزمون جداگانه قرار گیرند.

  • یک دیکشنری __test__ تعریف کنید که موضوعات آزمون رگرسیون را به رشته مستندات‌های حاوی موارد آزمون نگاشت می‌کند.

هنگامی که آزمون‌های خود را در یک ماژول قرار داده باشید، خود ماژول می‌تواند اجراکننده آزمون باشد. هنگامی که یک آزمون شکست می‌خورد، می‌توانید اجراکننده آزمون خود را به‌گونه‌ای تنظیم کنید که در حین اشکال‌زدایی مشکل، فقط doctest شکست‌خورده را دوباره اجرا کند. در اینجا یک مثال کمینه از چنین اجراکننده آزمونی آمده است:

if __name__ == '__main__':
    import doctest
    flags = doctest.REPORT_NDIFF|doctest.FAIL_FAST
    if len(sys.argv) > 1:
        name = sys.argv[1]
        if name in globals():
            obj = globals()[name]
        else:
            obj = __test__[name]
        doctest.run_docstring_examples(obj, globals(), name=name,
                                       optionflags=flags)
    else:
        fail, total = doctest.testmod(optionflags=flags)
        print(f"{fail} failures out of {total} tests")

پانویس‌ها