本節(jié)將詳細(xì)介紹doctest如何工作:查看它的文檔字符串,它如何查找交互式示例,它使用的執(zhí)行上下文,它如何處理異常以及如何使用選項標(biāo)志來控制其行為。這是編寫doctest示例時需要了解的信息; 有關(guān)在這些示例上實際運行doctest的信息,請參閱以下各節(jié)。
模塊docstring,以及所有函數(shù),類和方法文檔字符串被搜索。導(dǎo)入到模塊中的對象不被搜索。
另外,如果M.__test__存在且“為真”,則它必須是字典,并且每個條目將(字符串)名稱映射到函數(shù)對象,類對象或字符串。從中找到的函數(shù)和類對象文檔字符串M.__test__被搜索,字符串被視為文檔字符串。在輸出,一鍵K在M.__test__出現(xiàn)與名稱
<name of M>.__test__.K
找到的任何類都以相似的方式遞歸搜索,以測試其包含的方法和嵌套類中的文檔字符串。
在版本2.4中進(jìn)行了更改:“專用名稱”概念已被棄用且不再有記錄。
在大多數(shù)情況下,交互式控制臺會話的復(fù)制和粘貼工作正常,但doctest并不試圖精確模擬任何特定的Python shell。
>>> # comments are ignored
>>> x = 12
>>> x
12
>>> if x == 13:
... print "yes"
... else:
... print "no"
... print "NO"
... print "NO!!!"
...
no
NO
NO!!!
>>>
任何期望的輸出必須緊跟在包含代碼的最后一行'>>> '或'... '一行之后,并且預(yù)期的輸出(如果有的話)擴展到下一行'>>> '或全空白行。
細(xì)則:
在2.4版本中進(jìn)行了更改:將制表符擴展為空格是新的; 以前的版本試圖保留硬標(biāo)簽,結(jié)果令人困惑。
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
并且從開始示例的初始行中出現(xiàn)的預(yù)期輸出中刪除了許多主要的空白字符'>>>'。
默認(rèn)情況下,每次doctest發(fā)現(xiàn)一個文檔字符串進(jìn)行測試,它采用的是 淺拷貝的M的全局,使運行測試不會改變模塊真實的全局,因此,在一個測試M不能離開屑不小心讓另外一個背后測試工作。這意味著示例可以自由使用任何在頂層定義的M名稱,以及在運行的文檔字符串中定義的名稱。示例無法看到其他文檔中定義的名稱。
你可以通過強制使用自己的字典作為執(zhí)行上下文 globs=your_dict來testmod()或testfile()替代。
沒問題,只要回溯是該示例生成的唯一輸出:只需粘貼回溯。[1]由于回溯包含可能快速變化的細(xì)節(jié)(例如,確切的文件路徑和行號),所以這是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
該文檔測試成功,如果ValueError提出,詳情如圖所示。list.remove(x): x not in list
預(yù)期的異常輸出必須以追溯標(biāo)題開頭,該標(biāo)題可以是以下兩行中的任一行,縮寫與示例的第一行相同:
Traceback (most recent call last):
Traceback (innermost last):
traceback頭后面跟著一個可選的traceback堆棧,其內(nèi)容被doctest忽略?;厮荻褩Mǔ1缓雎?,或者從交互式會話逐字復(fù)制。
跟蹤堆棧后面是最有趣的部分:包含異常類型和細(xì)節(jié)的行。這通常是追溯的最后一行,但如果異常具有多行詳細(xì)信息,則可以跨越多行:
>>>
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: multi
line
detail
最后三行(以開始ValueError)與異常的類型和細(xì)節(jié)進(jìn)行比較,其余部分將被忽略。
最佳做法是省略追溯堆棧,除非它為示例增加了重要的文檔值。所以最后一個例子可能更好,因為:
>>>
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
...
ValueError: multi
line
detail
請注意,回溯處理非常特別。特別是,在改寫的例子中,使用...獨立于doctest的 ELLIPSIS選項。這個例子中的省略號可以省略,或者可以是三個(或三百個)逗號或數(shù)字,或者M(jìn)onty Python skit的縮進(jìn)記錄。
一些細(xì)節(jié)你應(yīng)該閱讀一次,但不需要記?。?/p>
1 1 File "", line 1 1 1 ^ SyntaxError: invalid syntax
由于顯示錯誤位置的行出現(xiàn)在異常類型和細(xì)節(jié)之前,因此它們不會被doctest檢查。例如,即使將^標(biāo)記放在錯誤的位置,也會通過以下測試:
1 1 File "", line 1 1 1 ^ SyntaxError: invalid syntax
許多選項標(biāo)志控制著doctest行為的各個方面。這些標(biāo)志的符號名稱作為模塊常量提供,可以按位或運算并傳遞給各種函數(shù)。這些名稱也可以在doctest指令中使用。
第一組選項定義測試語義,控制doctest如何確定實際輸出是否與示例預(yù)期輸出相匹配的方面:
doctest.DONT_ACCEPT_TRUE_FOR_1
默認(rèn)情況下,如果預(yù)期的輸出塊只包含1,只是含有實際輸出塊1或僅True被認(rèn)為是一個匹配,并類似地用于0對False。當(dāng)DONT_ACCEPT_TRUE_FOR_1指定時,不允許替換。缺省行為迎合了Python將許多函數(shù)的返回類型從整數(shù)更改為布爾值; 希望“小整數(shù)”輸出的doctests在這些情況下仍然有效。這個選項可能會消失,但不會持續(xù)數(shù)年。
doctest.DONT_ACCEPT_BLANKLINE
默認(rèn)情況下,如果預(yù)期的輸出塊包含僅包含字符串的行<BLANKLINE>,則該行將匹配實際輸出中的空行。由于真正的空行界定了預(yù)期的輸出,因此這是溝通預(yù)期空行的唯一方式。什么時候DONT_ACCEPT_BLANKLINE被指定,這個替代是不允許的。
doctest.NORMALIZE_WHITESPACE
指定時,所有空白(空格和換行符)都被視為相等。預(yù)期輸出中的任何空白序列都將與實際輸出中的任何空白序列相匹配。默認(rèn)情況下,空白必須完全匹配。NORMALIZE_WHITESPACE當(dāng)預(yù)期輸出的行很長時,并且您想要在源代碼中的多行中包裝它時,它特別有用。
doctest.ELLIPSIS
指定時,...預(yù)期輸出中的省略號標(biāo)記()可以匹配實際輸出中的任何子字符串。這包括跨越行邊界的子字符串和空的子字符串,所以最好保持簡單的使用。復(fù)雜的用途可能會導(dǎo)致相同類型的“oops,它匹配得太多了!” .*在正則表達(dá)式中很容易出現(xiàn)意外。
doctest.IGNORE_EXCEPTION_DETAIL
指定時,即使異常詳細(xì)信息不匹配,如果引發(fā)了期望類型的異常,那么期望異常的示例也會通過。例如,ValueError: 42如果引發(fā)的實際異常是預(yù)期的例子ValueError: 3*14,但會失敗,例如,如果TypeError引發(fā)。
它也會忽略Python 3 doctest報告中使用的模塊名稱。因此,無論測試是在Python 2.7還是Python 3.2(或更高版本)下運行,這兩種變體都可以與指定的標(biāo)志一起使用:
>>> raise CustomError('message')
Traceback (most recent call last):
CustomError: message
>>> raise CustomError('message')
Traceback (most recent call last):
my_module.CustomError: message
請注意,ELLIPSIS也可以用于忽略異常消息的詳細(xì)信息,但根據(jù)是否將模塊詳細(xì)信息作為異常名稱的一部分進(jìn)行打印,此類測試可能仍會失敗。使用IGNORE_EXCEPTION_DETAIL和來自Python 2.3的細(xì)節(jié)也是編寫文檔測試的唯一明確方式,它不關(guān)心異常細(xì)節(jié),但仍然在Python 2.3或更低版本中繼續(xù)傳遞(這些版本不支持doctest指令并將它們忽略為不相關(guān)的注釋) 。例如:
>>> (1, 2)[3] = 'moo'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment
雖然Python 2.4中的細(xì)節(jié)更改為“不”而不是“不”,但在Python 2.3以及更高版本的Python版本中通過了指定的標(biāo)志。
在版本2.7中更改:IGNORE_EXCEPTION_DETAIL現(xiàn)在也忽略了與包含被測異常的模塊有關(guān)的任何信息
doctest.SKIP
指定時,請不要運行該示例。這在doctest示例既可用作文檔也可用作測試用例的情況下非常有用,應(yīng)將其用于文檔目的,但不應(yīng)進(jìn)行檢查。例如,該示例的輸出可能是隨機的; 或者該示例可能依賴于測試驅(qū)動程序無法使用的資源。
SKIP標(biāo)志也可用于臨時“注釋”示例。
2.5版本中的新功能。
doctest.COMPARISON_FLAGS
將上面的所有比較標(biāo)志掩蓋起來。
第二組選項控制如何報告測試失?。?/p>
doctest.REPORT_UDIFF
指定時,涉及多行預(yù)期和實際輸出的故障將使用統(tǒng)一差異顯示。
doctest.REPORT_CDIFF
指定時,涉及多行預(yù)期輸出和實際輸出的故障將使用上下文差異顯示。
doctest.REPORT_NDIFF
指定時,difflib.Differ使用與常用ndiff.py實用程序相同的算法計算差異。這是標(biāo)記線內(nèi)和線間差異的唯一方法。例如,如果預(yù)期輸出的一行包含數(shù)字1,其中實際輸出包含字母l,則會插入一行,并在其中插入用于標(biāo)記不匹配列位置的插入符號。
doctest.REPORT_ONLY_FIRST_FAILURE
指定時,顯示每個doctest中的第一個失敗示例,但禁止所有其他示例的輸出。這將防止doctest報告因早期故障而中斷的正確示例; 但它也可能隱藏不正確的例子,不依靠第一次失敗而失敗。當(dāng)REPORT_ONLY_FIRST_FAILURE指定時,剩余的示例仍在運行,并仍然計入報告的故障總數(shù); 只有輸出被抑制。
doctest.REPORTING_FLAGS
將上面的所有報告標(biāo)記掩蓋起來。
新的2.4版本:常數(shù)DONT_ACCEPT_BLANKLINE,NORMALIZE_WHITESPACE,ELLIPSIS,IGNORE_EXCEPTION_DETAIL,REPORT_UDIFF,REPORT_CDIFF,REPORT_NDIFF,REPORT_ONLY_FIRST_FAILURE,COMPARISON_FLAGS和REPORTING_FLAGS添加。
還有一種方法可以注冊新的選項標(biāo)志名稱,但除非您打算doctest通過子類擴展內(nèi)部函數(shù),否則這種方法并不有用。
doctest.register_optionflag(name)
用給定名稱創(chuàng)建一個新選項標(biāo)志,并返回新標(biāo)志的整數(shù)值。register_optionflag()可用于子類化OutputChecker或DocTestRunner創(chuàng)建您的子類支持的新選項。register_optionflag()應(yīng)該總是使用以下習(xí)慣用法來調(diào)用:
MY_FLAG = register_optionflag('MY_FLAG')
New in version 2.4.
Doctest指令可用于修改單個示例的選項標(biāo)志。Doctest指令是遵循示例源代碼的特殊Python注釋:
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" \| ...
+or -和指令選項名稱之間不允許有空格。指令選項名稱可以是上面解釋的任何選項標(biāo)志名稱。
一個例子的doctest指令修改了doctest的這個例子的行為。使用+啟用這個名字的行為,或-將其禁用。
例如,這個測試通過:
>>> print 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]
如果沒有指令,它會失敗,這是因為實際輸出在單個數(shù)字列表元素之前沒有兩個空格,并且因為實際輸出在單行上。這個測試也通過了,并且還需要一個指令來做到這一點:
>>> print range(20) # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]
多條指令可用于單條物理線路,用逗號分隔:
>>> print range(20) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
如果單個示例使用多個指令注釋,則將它們合并:
>>> print range(20) # doctest: +ELLIPSIS
... # doctest: +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
如前例所示,您可以將...行添加到僅包含指令的示例中。當(dāng)一個例子對于指令很容易適合同一行時太長了,這會很有用:
>>> print range(5) + range(10,20) + range(30,40) + range(50,60)
... # doctest: +ELLIPSIS
[0, ..., 4, 10, ..., 19, 30, ..., 39, 50, ..., 59]
請注意,由于默認(rèn)情況下所有選項都被禁用,并且指令僅適用于它們出現(xiàn)的示例,因此啟用選項(通過+指令)通常是唯一有意義的選擇。但是,選項標(biāo)志也可以傳遞給運行doctests的函數(shù),建立不同的默認(rèn)值。在這種情況下,通過-指令禁用選項可能很有用。
2.4版新增功能:增加了對doctest指令的支持。
doctest嚴(yán)格要求在預(yù)期產(chǎn)出中要求完全匹配。如果即使單個字符不匹配,測試也會失敗。這可能會讓你感到驚訝,因為你確切地知道Python做了什么,并且不能保證輸出。例如,在打印字典時,Python不保證鍵值對將以任何特定的順序打印,因此像
>>> foo()
{"Hermione": "hippogryph", "Harry": "broomstick"}
很脆弱!一種解決方法是做
>>> foo() == {"Hermione": "hippogryph", "Harry": "broomstick"}
True
代替。另一個是要做的
>>> d = foo().items()
>>> d.sort()
>>> d
[('Harry', 'broomstick'), ('Hermione', 'hippogryph')]
還有其他的,但你明白了。
另一個不好的想法是打印嵌入對象地址的東西,比如
>>> id(1.0) # certain to fail some of the time
7948648
>>> class C: pass
>>> C() # the default repr() for instances embeds an address
<__main__.C instance at 0x00AC18F0>
ELLIPSIS指令為最后一個示例提供了一個很好的方法:
>>> C() #doctest: +ELLIPSIS
<__main__.C instance at 0x...>
浮點數(shù)也受到跨平臺的小輸出變化的影響,因為Python遵循平臺C庫進(jìn)行浮點格式化,而C庫在質(zhì)量上差別很大。
>>> 1./7 # risky
0.14285714285714285
>>> print 1./7 # safer
0.142857142857
>>> print round(1./7, 6) # much safer
0.142857
表格I/2.**J中的數(shù)字在所有平臺上都是安全的,而且我通常會編寫一些doctest的例子來生成這種格式的數(shù)字:
>>> 3./4 # utterly safe
0.75
簡單的分?jǐn)?shù)對于人們來說也更容易理解,并且這使得更好的文檔。
更多建議: