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_optionsdirective_options:directive_option(","directive_option)* directive_option:on_or_offdirective_option_nameon_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 بهکار میروند:
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است و برای بازنویسی گزینههای پیشفرض این مثال استفاده میشود. پرچمهای گزینهای که در این دیکشنری وجود نداشته باشند، روی مقدار پیشفرض خود باقی میمانند (طبق optionflagsDocTestRunner). بهطور پیشفرض، هیچ گزینهای تنظیم نشده است.
اشیای 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را ببینید.
اشیاء TestResults¶
اشیای 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.got¶
خروجی واقعی مثال.
- exception doctest.UnexpectedException(test, example, exc_info)¶
استثنایی که توسط
DocTestRunnerپرتاب میشود تا نشان دهد که یک مثال doctest استثنای غیرمنتظرهای را پرتاب کرده است. آرگومانهای سازنده برای مقداردهی اولیهی ویژگیهایی با همان نامها استفاده میشوند.
UnexpectedException ویژگیهای زیر را تعریف میکند:
- UnexpectedException.exc_info¶
یک تاپل حاوی اطلاعات درباره استثنای غیرمنتظره، همانطور که توسط
sys.exc_info()برگردانده میشود.
تریبون¶
همانطور که در مقدمه ذکر شد، doctest گسترش یافته است تا سه کاربرد اصلی داشته باشد:
بررسی مثالها در رشته مستندات.
آزمون رگرسیون.
مستندات قابلاجرا / آزموننویسی ادبی.
این کاربردها نیازمندیهای متفاوتی دارند و مهم است که میان آنها تمایز قائل شوید. بهویژه، پر کردن رشته مستنداتتان با موارد آزمون مبهم، باعث ایجاد مستندات بد میشود.
هنگام نوشتن یک رشتهی مستندسازی، مثالهای آن را با دقت انتخاب کنید. این کار ظرافتی دارد که باید آموخته شود — ممکن است در ابتدا برایتان طبیعی نباشد. مثالها باید ارزش واقعی به مستندات بیفزایند. یک مثال خوب اغلب میتواند ارزشی برابر با کلمات بسیاری داشته باشد. اگر با دقت تهیه شوند، مثالها برای کاربران شما بینهایت ارزشمند خواهند بود و با گذشت سالها و تغییر شرایط، زمانی را که صرف گردآوری آنها شده است، چندین برابر جبران خواهند کرد. هنوز هم از تعداد دفعاتی که یکی از مثالهای 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")
پانویسها