doctest --- 測(cè)試交互性的Python示例?

** 源代碼 ** Lib/doctest.py


doctest 模塊尋找像Python交互式代碼的文本,然后執(zhí)行這些代碼來(lái)確保它們的確就像展示的那樣正確運(yùn)行,有許多方法來(lái)使用doctest:

  • 通過(guò)驗(yàn)證所有交互式示例仍然按照記錄的方式工作,以此來(lái)檢查模塊的文檔字符串是否是最新的。

  • 通過(guò)驗(yàn)證來(lái)自一個(gè)測(cè)試文件或一個(gè)測(cè)試對(duì)象的交互式示例按預(yù)期工作,來(lái)進(jìn)行回歸測(cè)試。

  • 為一個(gè)包寫(xiě)指導(dǎo)性的文檔,用輸入輸出的例子來(lái)說(shuō)明。 取決于是強(qiáng)調(diào)例子還是說(shuō)明性的文字,這有一種 "文本測(cè)試 "或 "可執(zhí)行文檔 "的風(fēng)格。

下面是一個(gè)小卻完整的示例模塊:

"""
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()

如果你直接在命令行里運(yùn)行 example.pydoctest 將發(fā)揮它的作用。

$ python example.py
$

沒(méi)有輸出! 這很正常,這意味著所有的例子都成功了。 把 -v 傳給腳本,doctest 會(huì)打印出它所嘗試的詳細(xì)日志,并在最后打印出一個(gè)總結(jié)。

$ 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

以此類(lèi)推,最終以:

Trying:
    factorial(1e100)
Expecting:
    Traceback (most recent call last):
        ...
    OverflowError: n too large
ok
2 items passed all tests:
   1 tests in __main__
   8 tests in __main__.factorial
9 tests in 2 items.
9 passed and 0 failed.
Test passed.
$

這就是對(duì)于高效地使用 doctest 你所需要知道的一切!開(kāi)始上手吧。 下面的章節(jié)提供了完整的細(xì)節(jié)。 請(qǐng)注意,在標(biāo)準(zhǔn)的 Python 測(cè)試套件和庫(kù)中有許多 doctest 的例子。特別有用的例子可以在標(biāo)準(zhǔn)測(cè)試文件 Lib/test/test_doctest.py 中找到。

簡(jiǎn)單用法:檢查Docstrings中的示例?

開(kāi)始使用doctest的最簡(jiǎn)單方法(但不一定是你將繼續(xù)這樣做的方式)是結(jié)束每個(gè)模塊 M 使用:

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

doctest 會(huì)隨后檢查模塊 M 中的文檔字符串。

以腳本形式運(yùn)行該模塊會(huì)使文檔中的例子得到執(zhí)行和驗(yàn)證:

python M.py

這不會(huì)顯示任何東西,除非一個(gè)例子失敗了,在這種情況下,失敗的例子和失敗的原因會(huì)被打印到stdout,最后一行的輸出是``***Test Failed*** N failures.``,其中*N*是失敗的例子的數(shù)量。

用``-v``來(lái)運(yùn)行它來(lái)切換,而不是:

python M.py -v

并將所有嘗試過(guò)的例子的詳細(xì)報(bào)告打印到標(biāo)準(zhǔn)輸出,最后還有各種總結(jié)。

你可以通過(guò)向 testmod() 傳遞 verbose=True 來(lái)強(qiáng)制執(zhí)行 verbose 模式,或者通過(guò)傳遞 verbose=False 來(lái)禁止它。 在這兩種情況下,sys.argv 都不會(huì)被 testmod() 檢查(所以傳遞 -v 或不傳遞都沒(méi)有影響)。

還有一個(gè)命令行快捷方式用于運(yùn)行 testmod()。 你可以指示Python解釋器直接從標(biāo)準(zhǔn)庫(kù)中運(yùn)行doctest模塊,并在命令行中傳遞模塊名稱:

python -m doctest -v example.py

這將導(dǎo)入 example.py 作為一個(gè)獨(dú)立的模塊,并對(duì)其運(yùn)行 testmod()。 注意,如果該文件是一個(gè)包的一部分,并且從該包中導(dǎo)入了其他子模塊,這可能無(wú)法正確工作。

關(guān)于 testmod() 的更多信息,請(qǐng)參見(jiàn) 基本API 部分。

簡(jiǎn)單的用法:檢查文本文件中的例子?

doctest 的另一個(gè)簡(jiǎn)單應(yīng)用是測(cè)試文本文件中的交互式例子。 這可以用 testfile() 函數(shù)來(lái)完成;:

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

這個(gè)簡(jiǎn)短的腳本執(zhí)行并驗(yàn)證文件 example.txt 中包含的任何交互式 Python 示例。該文件的內(nèi)容被當(dāng)作一個(gè)巨大的文檔串來(lái)處理;該文件不需要包含一個(gè)Python程序!例如,也許 example.txt 包含以下內(nèi)容:

The ``example`` module
======================

Using ``factorial``
-------------------

This is an example text file in reStructuredText format.  First import
``factorial`` from the ``example`` module:

    >>> from example import factorial

Now use it:

    >>> factorial(6)
    120

運(yùn)行``doctest.testfile("example.txt")``,然后發(fā)現(xiàn)這個(gè)文檔中的錯(cuò)誤:

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

testmod() 一樣, testfile() 不會(huì)顯示任何東西,除非一個(gè)例子失敗。 如果一個(gè)例子失敗了,那么失敗的例子和失敗的原因?qū)⒈淮蛴〉絪tdout,使用的格式與 testmod() 相同。

默認(rèn)情況下,testfile() 在調(diào)用模塊的目錄中尋找文件。參見(jiàn)章節(jié) 基本API,了解可用于告訴它在其他位置尋找文件的可選參數(shù)的描述。

testmod() 一樣,testfile() 的詳細(xì)程度可以通過(guò)命令行 -v 切換或可選的關(guān)鍵字參數(shù) verbose 來(lái)設(shè)置。

還有一個(gè)命令行快捷方式用于運(yùn)行 testfile()。 你可以指示Python解釋器直接從標(biāo)準(zhǔn)庫(kù)中運(yùn)行doctest模塊,并在命令行中傳遞文件名:

python -m doctest -v example.txt

因?yàn)槲募麤](méi)有以 .py 結(jié)尾,doctest 推斷它必須用 testfile() 運(yùn)行,而不是 testmod()。

關(guān)于 testfile() 的更多信息,請(qǐng)參見(jiàn) 基本API 一節(jié)。

它是如何工作的?

這一節(jié)詳細(xì)研究了doctest的工作原理:它查看哪些文檔串,它如何找到交互式的用例,它使用什么執(zhí)行環(huán)境,它如何處理異常,以及如何用選項(xiàng)標(biāo)志來(lái)控制其行為。這是你寫(xiě)doctest例子所需要知道的信息;關(guān)于在這些例子上實(shí)際運(yùn)行doctest的信息,請(qǐng)看下面的章節(jié)。

哪些文件串被檢查了??

模塊的文檔串以及所有函數(shù)、類(lèi)和方法的文檔串都將被搜索。 導(dǎo)入模塊的對(duì)象不被搜索。

此外,如果 M.__test__ 存在并且 "為真值",則它必須是一個(gè)字典,其中每個(gè)條目都將一個(gè)(字符串)名稱映射到一個(gè)函數(shù)對(duì)象、類(lèi)對(duì)象或字符串。 從 M.__test__ 找到的函數(shù)和類(lèi)對(duì)象的文檔字符串會(huì)被搜索,而字符串會(huì)被當(dāng)作文檔字符串來(lái)處理。 在輸出時(shí),每個(gè)鍵 KM.__test__ 中都顯示為其名稱

<name of M>.__test__.K

任何發(fā)現(xiàn)的類(lèi)都會(huì)以類(lèi)似的方式進(jìn)行遞歸搜索,以測(cè)試其包含的方法和嵌套類(lèi)中的文檔串。

文檔串的例子是如何被識(shí)別的??

在大多數(shù)情況下,對(duì)交互式控制臺(tái)會(huì)話的復(fù)制和粘貼功能工作得很好,但是 doctest 并不試圖對(duì)任何特定的 Python shell 進(jìn)行精確的模擬。

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

任何預(yù)期的輸出必須緊隨包含代碼的最后 '>>> ''... ' 行,預(yù)期的輸出(如果有的話)延伸到下一 '>>> ' 行或全空白行。

fine輸出:

  • 預(yù)期輸出不能包含一個(gè)全白的行,因?yàn)檫@樣的行被認(rèn)為是預(yù)期輸出的結(jié)束信號(hào)。 如果預(yù)期的輸出包含一個(gè)空行,在你的測(cè)試?yán)又?,在每一個(gè)預(yù)期有空行的地方加上``<BLANKLINE>``。

  • 所有硬制表符都被擴(kuò)展為空格,使用 8 列的制表符。由測(cè)試代碼生成的輸出中的制表符不會(huì)被修改。 因?yàn)闃颖据敵鲋械娜魏斡仓票矸紩?huì)被擴(kuò)展,這意味著如果代碼輸出包括硬制表符,文檔測(cè)試通過(guò)的唯一方法是 NORMALIZE_WHITESPACE 選項(xiàng)或者 指令 是有效的。 另外,測(cè)試可以被重寫(xiě),以捕獲輸出并將其與預(yù)期值進(jìn)行比較,作為測(cè)試的一部分。這種對(duì)源碼中標(biāo)簽的處理是通過(guò)試錯(cuò)得出的,并被證明是最不容易出錯(cuò)的處理方式。通過(guò)編寫(xiě)一個(gè)自定義的 DocTestParser 類(lèi),可以使用一個(gè)不同的算法來(lái)處理標(biāo)簽。

  • 向stdout的輸出被捕獲,但不向stderr輸出(異常回溯通過(guò)不同的方式被捕獲)。

  • 如果你在交互式會(huì)話中通過(guò)反斜線續(xù)行,或出于任何其他原因使用反斜線,你應(yīng)該使用原始文件串,它將完全保留你輸入的反斜線:

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

    否則,反斜杠將被解釋為字符串的一部分。例如,上面的``n``會(huì)被解釋為一個(gè)換行符。 另外,你可以在doctest版本中把每個(gè)反斜杠加倍(而不使用原始字符串):

    >>>
    >>> 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
    

    并從預(yù)期的輸出中剝離出與開(kāi)始該例子的初始 '>>> ' 行中出現(xiàn)的同樣多的前導(dǎo)空白字符。

什么是執(zhí)行上下文??

默認(rèn)情況下,每次 doctest 找到要測(cè)試的文檔串時(shí),它都會(huì)使用 M 的*淺層副本*,這樣運(yùn)行測(cè)試就不會(huì)改變模塊的真正全局變量,而且 M 的一個(gè)測(cè)試也不會(huì)留下臨時(shí)變量,從而意外地讓另一個(gè)測(cè)試通過(guò)。這意味著例子可以自由地使用 M 中的任何頂級(jí)定義的名字,以及正在運(yùn)行的文檔串中早期定義的名字。用例不能看到其他文檔串中定義的名字。

你可以通過(guò)將``globs=your_dict``傳遞給 testmod()testfile() 來(lái)強(qiáng)制使用你自己的dict作為執(zhí)行環(huán)境。

異常如何處理??

沒(méi)問(wèn)題,只要回溯是這個(gè)例子產(chǎn)生的唯一輸出:只要粘貼回溯即可。1 由于回溯所包含的細(xì)節(jié)可能會(huì)迅速變化(例如,確切的文件路徑和行號(hào)),這是doctest努力使其接受的內(nèi)容具有靈活性的一種情況。

簡(jiǎn)單實(shí)例:

>>>
>>> [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 被觸發(fā),該測(cè)試就會(huì)成功,list.remove(x): x not in list 的細(xì)節(jié)如圖所示。

異常的預(yù)期輸出必須以回溯頭開(kāi)始,可以是以下兩行中的任何一行,縮進(jìn)程度與例子中的第一行相同:

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

回溯頭的后面是一個(gè)可選的回溯堆棧,其內(nèi)容被doctest忽略。 回溯堆棧通常是省略的,或者從交互式會(huì)話中逐字復(fù)制的。

回溯堆棧的后面是最有用的部分:包含異常類(lèi)型和細(xì)節(jié)的一行(幾行)。 這通常是回溯的最后一行,但如果異常有多行細(xì)節(jié),則可以延伸到多行:

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

最后三行(以 ValueError 開(kāi)頭)將與異常的類(lèi)型和細(xì)節(jié)進(jìn)行比較,其余的被忽略。

最佳實(shí)踐是省略回溯棧,除非它為這個(gè)例子增加了重要的文檔價(jià)值。 因此,最后一個(gè)例子可能更好,因?yàn)?

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

請(qǐng)注意,回溯的處理方式非常特別。 特別是,在重寫(xiě)的例子中,... 的使用與 doctest 的 ELLIPSIS 選項(xiàng)無(wú)關(guān)。 該例子中的省略號(hào)可以不寫(xiě),也可以是三個(gè)(或三百個(gè))逗號(hào)或數(shù)字,或者是一個(gè)縮進(jìn)的 Monty Python 短劇的劇本。

有些細(xì)節(jié)你應(yīng)該讀一遍,但不需要記?。?/p>

  • Doctest 不能猜測(cè)你的預(yù)期輸出是來(lái)自異?;厮葸€是來(lái)自普通打印。 因此,例如,一個(gè)期望 ValueError: 42 is prime 的用例將通過(guò)測(cè)試,無(wú)論 ValueError 是真的被觸發(fā),或者該用例只是打印了該回溯文本。 在實(shí)踐中,普通輸出很少以回溯標(biāo)題行開(kāi)始,所以這不會(huì)產(chǎn)生真正的問(wèn)題。

  • 回溯堆棧的每一行(如果有的話)必須比例子的第一行縮進(jìn), 或者 以一個(gè)非字母數(shù)字的字符開(kāi)始?;厮蓊^之后的第一行縮進(jìn)程度相同,并且以字母數(shù)字開(kāi)始,被認(rèn)為是異常細(xì)節(jié)的開(kāi)始。當(dāng)然,這對(duì)真正的回溯來(lái)說(shuō)是正確的事情。

  • 當(dāng) IGNORE_EXCEPTION_DETAIL doctest 選項(xiàng)被指定時(shí),最左邊的冒號(hào)后面的所有內(nèi)容以及異常名稱中的任何模塊信息都被忽略。

  • 交互式 shell 省略了一些 SyntaxError 的回溯頭行。但 doctest 使用回溯頭行來(lái)區(qū)分異常和非異常。所以在罕見(jiàn)的情況下,如果你需要測(cè)試一個(gè)省略了回溯頭的 SyntaxError,你將需要手動(dòng)添加回溯頭行到你的測(cè)試用例中。

  • For some exceptions, Python displays the position of the error using ^ markers and tildes:

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

    由于顯示錯(cuò)誤位置的行在異常類(lèi)型和細(xì)節(jié)之前,它們不被doctest檢查。 例如,下面的測(cè)試會(huì)通過(guò),盡管它把``^``標(biāo)記放在了錯(cuò)誤的位置:

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

選項(xiàng)標(biāo)記?

一系列選項(xiàng)旗標(biāo)控制著 doctest 的各方面行為。 旗標(biāo)的符號(hào)名稱以模塊常量的形式提供,可以一起 bitwise ORed 并傳遞給各種函數(shù)。 這些名稱也可以在 doctest directives 中使用,并且可以通過(guò) -o 選項(xiàng)傳遞給 doctest 命令行接口。

3.4 新版功能: 命令行選項(xiàng) -o 。

第一組選項(xiàng)定義了測(cè)試語(yǔ)義,控制doctest如何決定實(shí)際輸出是否與用例的預(yù)期輸出相匹配方面的問(wèn)題。

doctest.DONT_ACCEPT_TRUE_FOR_1?

默認(rèn)情況下,如果一個(gè)預(yù)期的輸出塊只包含 1,那么實(shí)際的輸出塊只包含 1 或只包含 True 就被認(rèn)為是匹配的,同樣,0False 也是如此。 當(dāng) DONT_ACCEPT_TRUE_FOR_1 被指定時(shí),兩種替換都不允許。 默認(rèn)行為是為了適應(yīng) Python 將許多函數(shù)的返回類(lèi)型從整數(shù)改為布爾值;期望 "小整數(shù)" 輸出的測(cè)試在這些情況下仍然有效。 這個(gè)選項(xiàng)可能會(huì)消失,但不會(huì)在幾年內(nèi)消失。

doctest.DONT_ACCEPT_BLANKLINE?

默認(rèn)情況下,如果一個(gè)預(yù)期輸出塊包含一個(gè)只包含字符串 <BLANKLINE> 的行,那么該行將與實(shí)際輸出中的一個(gè)空行相匹配。 因?yàn)橐粋€(gè)真正的空行是對(duì)預(yù)期輸出的限定,這是傳達(dá)預(yù)期空行的唯一方法。 當(dāng) DONT_ACCEPT_BLANKLINE 被指定時(shí),這種替換是不允許的。

doctest.NORMALIZE_WHITESPACE?

當(dāng)指定時(shí),所有的空白序列(空白和換行)都被視為相等。預(yù)期輸出中的任何空白序列將與實(shí)際輸出中的任何空白序列匹配。默認(rèn)情況下,空白必須完全匹配。 NORMALIZE_WHITESPACE 在預(yù)期輸出非常長(zhǎng)的一行,而你想把它包在源代碼的多行中時(shí)特別有用。

doctest.ELLIPSIS?

當(dāng)指定時(shí),預(yù)期輸出中的省略號(hào)(...)可以匹配實(shí)際輸出中的任何子串。這包括跨行的子串和空子串,所以最好保持簡(jiǎn)單的用法。復(fù)雜的用法會(huì)導(dǎo)致與``.*``在正則表達(dá)式中容易出現(xiàn)的 "oops, it matched too much!"相同的意外情況。

doctest.IGNORE_EXCEPTION_DETAIL?

When specified, doctests expecting exceptions pass so long as an exception of the expected type is raised, even if the details (message and fully-qualified exception name) don't match.

For example, an example expecting ValueError: 42 will pass if the actual exception raised is ValueError: 3*14, but will fail if, say, a TypeError is raised instead. It will also ignore any fully-qualified name included before the exception class, which can vary between implementations and versions of Python and the code/libraries in use. Hence, all three of these variations will work with the flag specified:

>>>
>>> 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

Note that ELLIPSIS can also be used to ignore the details of the exception message, but such a test may still fail based on whether the module name is present or matches exactly.

在 3.2 版更改: IGNORE_EXCEPTION_DETAIL 現(xiàn)在也忽略了與包含被測(cè)異常的模塊有關(guān)的任何信息。

doctest.SKIP?

當(dāng)指定時(shí),完全不運(yùn)行這個(gè)用例。 這在doctest用例既是文檔又是測(cè)試案例的情況下很有用,一個(gè)例子應(yīng)該包括在文檔中,但不應(yīng)該被檢查。例如,這個(gè)例子的輸出可能是隨機(jī)的;或者這個(gè)例子可能依賴于測(cè)試驅(qū)動(dòng)程序所不能使用的資源。

SKIP標(biāo)志也可用于臨時(shí) "注釋" 用例。

doctest.COMPARISON_FLAGS?

一個(gè)比特或運(yùn)算將上述所有的比較標(biāo)志放在一起。

第二組選項(xiàng)控制測(cè)試失敗的報(bào)告方式:

doctest.REPORT_UDIFF?

當(dāng)指定時(shí),涉及多行預(yù)期和實(shí)際輸出的故障將使用統(tǒng)一的差異來(lái)顯示。

doctest.REPORT_CDIFF?

當(dāng)指定時(shí),涉及多行預(yù)期和實(shí)際輸出的故障將使用上下文差異來(lái)顯示。

doctest.REPORT_NDIFF?

當(dāng)指定時(shí),差異由``difflib.Differ``來(lái)計(jì)算,使用與流行的:file:ndiff.py`工具相同的算法。這是唯一一種標(biāo)記行內(nèi)和行間差異的方法。 例如,如果一行預(yù)期輸出包含數(shù)字``1`,而實(shí)際輸出包含字母``l``,那么就會(huì)插入一行,用圓點(diǎn)標(biāo)記不匹配的列位置。

doctest.REPORT_ONLY_FIRST_FAILURE?

當(dāng)指定時(shí),在每個(gè) doctest 中顯示第一個(gè)失敗的用例,但隱藏所有其余用例的輸出。 這將防止 doctest 報(bào)告由于先前的失敗而中斷的正確用例;但也可能隱藏獨(dú)立于第一個(gè)失敗的不正確用例。 當(dāng) REPORT_ONLY_FIRST_FAILURE 被指定時(shí),其余的用例仍然被運(yùn)行,并且仍然計(jì)入報(bào)告的失敗總數(shù);只是輸出被隱藏了。

doctest.FAIL_FAST?

當(dāng)指定時(shí),在第一個(gè)失敗的用例后退出,不嘗試運(yùn)行其余的用例。因此,報(bào)告的失敗次數(shù)最多為1。這個(gè)標(biāo)志在調(diào)試時(shí)可能很有用,因?yàn)榈谝粋€(gè)失敗后的用例甚至不會(huì)產(chǎn)生調(diào)試輸出。

doctest命令行接受選項(xiàng)``-f``作為``-o FAIL_FAST``的簡(jiǎn)潔形式。

3.4 新版功能.

doctest.REPORTING_FLAGS?

一個(gè)比特或操作將上述所有的報(bào)告標(biāo)志組合在一起。

還有一種方法可以注冊(cè)新的選項(xiàng)標(biāo)志名稱,不過(guò)這并不有用,除非你打算通過(guò)子類(lèi)來(lái)擴(kuò)展 doctest 內(nèi)部。

doctest.register_optionflag(name)?

用給定的名稱創(chuàng)建一個(gè)新的選項(xiàng)標(biāo)志,并返回新標(biāo)志的整數(shù)值。 register_optionflag() 可以在繼承 OutputCheckerDocTestRunner 時(shí)使用,以創(chuàng)建子類(lèi)支持的新選項(xiàng)。 register_optionflag() 應(yīng)始終使用以下方式調(diào)用:

MY_FLAG = register_optionflag('MY_FLAG')

指令?

Doctest指令可以用來(lái)修改單個(gè)例子的 option flags 。 Doctest指令是在一個(gè)用例的源代碼后面的特殊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" \| ...

+- 與指令選項(xiàng)名稱之間不允許有空格。 指令選項(xiàng)名稱可以是上面解釋的任何一個(gè)選項(xiàng)標(biāo)志名稱。

一個(gè)用例的 doctest 指令可以修改 doctest 對(duì)該用例的行為。 使用 + 來(lái)啟用指定的行為,或者使用 - 來(lái)禁用它。

For example, this test passes:

>>>
>>> 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]

Without the directive it would fail, both because the actual output doesn't have two blanks before the single-digit list elements, and because the actual output is on a single line. This test also passes, and also requires a directive to do so:

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

Multiple directives can be used on a single physical line, separated by commas:

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

If multiple directive comments are used for a single example, then they are combined:

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

As the previous example shows, you can add ... lines to your example containing only directives. This can be useful when an example is too long for a directive to comfortably fit on the same line:

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

請(qǐng)注意,由于所有的選項(xiàng)都是默認(rèn)禁用的,而指令只適用于它們出現(xiàn)的用例,所以啟用選項(xiàng) (通過(guò)指令中的 +) 通常是唯一有意義的選擇。 然而,選項(xiàng)標(biāo)志也可以被傳遞給運(yùn)行測(cè)試的函數(shù),建立不同的默認(rèn)值。 在這種情況下,通過(guò)指令中的 - 來(lái)禁用一個(gè)選項(xiàng)可能是有用的。

警告?

doctest 是嚴(yán)格地要求在預(yù)期輸出中完全匹配。 如果哪怕只有一個(gè)字符不匹配,測(cè)試就會(huì)失敗。 這可能會(huì)讓你吃驚幾次,在你確切地了解到 Python 對(duì)輸出的保證和不保證之前。 例如,當(dāng)打印一個(gè)集合時(shí),Python 不保證元素以任何特定的順序被打印出來(lái),所以像:

>>>
>>> foo()
{"Hermione", "Harry"}

是不可靠的!一個(gè)變通方法是用:

>>>
>>> foo() == {"Hermione", "Harry"}
True

來(lái)取代。另一個(gè)是使用

>>>
>>> d = sorted(foo())
>>> d
['Harry', 'Hermione']

還有其他的問(wèn)題,但你會(huì)明白的。

Another bad idea is to print things that embed an object address, like

>>>
>>> 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>

The ELLIPSIS directive gives a nice approach for the last example:

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

浮點(diǎn)數(shù)在不同的平臺(tái)上也會(huì)有小的輸出變化,因?yàn)镻ython在浮點(diǎn)數(shù)的格式化上依賴于平臺(tái)的C庫(kù),而C庫(kù)在這個(gè)問(wèn)題上的質(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ù)字在所有的平臺(tái)上都是安全的,我經(jīng)常設(shè)計(jì)一些測(cè)試的用例來(lái)產(chǎn)生該形式的數(shù):

>>>
>>> 3./4  # utterly safe
0.75

簡(jiǎn)單的分?jǐn)?shù)也更容易讓人理解,這也使得文件更加完善。

基本API?

函數(shù) testmod()testfile() 為 doctest 提供了一個(gè)簡(jiǎn)單的接口,應(yīng)該足以滿足大多數(shù)基本用途。關(guān)于這兩個(gè)函數(shù)的不太正式的介紹,請(qǐng)參見(jiàn) 簡(jiǎn)單用法:檢查Docstrings中的示例簡(jiǎn)單的用法:檢查文本文件中的例子 部分。

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*,所有的參數(shù)都是可選的,而且應(yīng)該以關(guān)鍵字的形式指定。

測(cè)試名為 filename 的文件中的用例。 返回``(failure_count, test_count)``。

可選參數(shù) module_relative 指定了文件名的解釋方式。

  • 如果 module_relativeTrue (默認(rèn)),那么 filename 指定一個(gè)獨(dú)立于操作系統(tǒng)的模塊相對(duì)路徑。 默認(rèn)情況下,這個(gè)路徑是相對(duì)于調(diào)用模塊的目錄的;但是如果指定了 package 參數(shù),那么它就是相對(duì)于該包的。 為了保證操作系統(tǒng)的獨(dú)立性, filename 應(yīng)該使用字符來(lái)分隔路徑段,并且不能是一個(gè)絕對(duì)路徑 (即不能以 / 開(kāi)始)。

  • 如果 module_relativeFalse,那么 filename 指定了一個(gè)操作系統(tǒng)特定的路徑。路徑可以是絕對(duì)的,也可以是相對(duì)的;相對(duì)路徑是相對(duì)于當(dāng)前工作目錄而言的。

可選參數(shù) name 給出了測(cè)試的名稱;默認(rèn)情況下,或者如果是``None``,那么使用``os.path.basename(filename)``。

可選參數(shù) package 是一個(gè) Python 包或一個(gè) Python 包的名字,其目錄應(yīng)被用作模塊相關(guān)文件名的基礎(chǔ)目錄。 如果沒(méi)有指定包,那么調(diào)用模塊的目錄將作為模塊相關(guān)文件名的基礎(chǔ)目錄。如果 module_relative 是``False``,那么指定 package 是錯(cuò)誤的。

可選參數(shù) globs 給出了一個(gè)在執(zhí)行示例時(shí)用作全局變量的dict。 這個(gè)dict的一個(gè)新的淺層副本將為doctest創(chuàng)建,因此它的用例將從一個(gè)干凈的地方開(kāi)始。默認(rèn)情況下,或者如果為``None``,使用一個(gè)新的空dict。

可選參數(shù) extraglobs 給出了一個(gè)合并到用于執(zhí)行用例全局變量中的dict。 這就像 dict.update() 一樣:如果 globsextraglobs 有一個(gè)共同的鍵,那么 extraglobs 中的相關(guān)值會(huì)出現(xiàn)在合并的dict中。 默認(rèn)情況下,或者為``None`` ,則不使用額外的全局變量。這是一個(gè)高級(jí)功能,允許對(duì) doctest 進(jìn)行參數(shù)化。例如,可以為一個(gè)基類(lèi)寫(xiě)一個(gè)測(cè)試,使用該類(lèi)的通用名稱,然后通過(guò)傳遞一個(gè) extraglobs dict,將通用名稱映射到要測(cè)試的子類(lèi),從而重復(fù)用于測(cè)試任何數(shù)量的子類(lèi)。

可選的參數(shù) verbose 如果為真值會(huì)打印很多東西,如果為假值則只打印失敗信息;默認(rèn)情況下,或者為 None,只有當(dāng) '-v'sys.argv 中時(shí)才為真值。

可選參數(shù) report 為T(mén)rue時(shí),在結(jié)尾處打印一個(gè)總結(jié),否則在結(jié)尾處什么都不打印。 在verbose模式下,總結(jié)是詳細(xì)的,否則總結(jié)是非常簡(jiǎn)短的(事實(shí)上,如果所有的測(cè)試都通過(guò)了,總結(jié)就是空的)。

可選參數(shù) optionflags (默認(rèn)值為0)是選項(xiàng)標(biāo)志的 bitwise OR 。參見(jiàn)章節(jié) 選項(xiàng)標(biāo)記 。

可選參數(shù) raise_on_error 默認(rèn)為False。 如果是True,在一個(gè)用例中第一次出現(xiàn)失敗或意外的異常時(shí),會(huì)觸發(fā)一個(gè)異常。這允許對(duì)失敗進(jìn)行事后調(diào)試。默認(rèn)行為是繼續(xù)運(yùn)行例子。

可選參數(shù) parser 指定一個(gè) DocTestParser (或子類(lèi)),它應(yīng)該被用來(lái)從文件中提取測(cè)試。它默認(rèn)為一個(gè)普通的解析器(即``DocTestParser()``)。

可選參數(shù) encoding 指定了一個(gè)編碼,應(yīng)該用來(lái)將文件轉(zhuǎn)換為unicode。

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

所有的參數(shù)都是可選的,除了 m 之外,都應(yīng)該以關(guān)鍵字的形式指定。

測(cè)試從模塊 m (或模塊 __main__ ,如果 m 沒(méi)有被提供或?yàn)閌`None``)可達(dá)到的函數(shù)和類(lèi)的文檔串中的用例,從``m.__doc__``開(kāi)始。

也測(cè)試從dict m.__test__ 可達(dá)到的用例,如果它存在并且不是 Nonem.__test__ 將名字(字符串)映射到函數(shù)、類(lèi)和字符串;函數(shù)和類(lèi)的文檔串被搜索到的用例;字符串被直接搜索到,就像它們是文檔串一樣。

只搜索附屬于模塊 m 中的對(duì)象的文檔串。

返回 (failure_count, test_count) 。

可選參數(shù) name 給出了模塊的名稱;默認(rèn)情況下,或者如果為 None ,則為 m.__name__ 。

可選參數(shù) exclude_empty 默認(rèn)為假值。 如果為真值,沒(méi)有找到任何 doctest 的對(duì)象將被排除在考慮范圍之外。 默認(rèn)情況下是向后兼容,所以仍然使用 doctest.master.summarize()testmod() 的代碼會(huì)繼續(xù)得到?jīng)]有測(cè)試的對(duì)象的輸出。 較新的 DocTestFinder 構(gòu)造器的 exclude_empty 參數(shù)默認(rèn)為真值。

可選參數(shù) extraglobs 、 verbose 、 report 、 optionflags 、 raise_on_errorglobs 與上述函數(shù) testfile() 的參數(shù)相同,只是 globs 默認(rèn)為 m.__dict__ 。

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

與對(duì)象 f 相關(guān)的測(cè)試用例;例如, f 可以是一個(gè)字符串、一個(gè)模塊、一個(gè)函數(shù)或一個(gè)類(lèi)對(duì)象。

dict 參數(shù) globs 的淺層拷貝被用于執(zhí)行環(huán)境。

可選參數(shù) name 在失敗信息中使用,默認(rèn)為 "NoName" 。

如果可選參數(shù) verbose 為真,即使沒(méi)有失敗也會(huì)產(chǎn)生輸出。 默認(rèn)情況下,只有在用例失敗的情況下才會(huì)產(chǎn)生輸出。

可選參數(shù) compileflags 給出了Python編譯器在運(yùn)行例子時(shí)應(yīng)該使用的標(biāo)志集。默認(rèn)情況下,或者如果為 None ,標(biāo)志是根據(jù) globs 中發(fā)現(xiàn)的未來(lái)特征集推導(dǎo)出來(lái)的。

可選參數(shù) optionflags 的作用與上述 testfile() 函數(shù)中的相同。

Unittest API?

doctest 提供了兩個(gè)函數(shù),可以用來(lái)從模塊和包含測(cè)試的文本文件中創(chuàng)建 unittest 測(cè)試套件。 要與 unittest 測(cè)試發(fā)現(xiàn)集成,請(qǐng)?jiān)谀愕臏y(cè)試模塊中包含一個(gè) load_tests() 函數(shù)::

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

有兩個(gè)主要函數(shù)用于從文本文件和帶doctest的模塊中創(chuàng)建 unittest.TestSuite 實(shí)例。

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

將一個(gè)或多個(gè)文本文件中的doctest測(cè)試轉(zhuǎn)換為一個(gè) unittest.TestSuite 。

返回的 unittest.TestSuite 將由 unittest 框架運(yùn)行,并運(yùn)行每個(gè)文件中的交互式示例。如果任何文件中的用例失敗了,那么合成的單元測(cè)試就會(huì)失敗,并觸發(fā)一個(gè) failureException 異常,顯示包含該測(cè)試的文件名和一個(gè)(有時(shí)是近似的)行號(hào)。

傳遞一個(gè)或多個(gè)要檢查的文本文件的路徑(作為字符串)。

選項(xiàng)可以作為關(guān)鍵字參數(shù)提供:

可選參數(shù) module_relative 指定了 paths 中的文件名應(yīng)該如何解釋。

  • 如果*module_relative*是 True (默認(rèn)值),那么 paths 中的每個(gè)文件名都指定了一個(gè)獨(dú)立于操作系統(tǒng)的模塊相對(duì)路徑。 默認(rèn)情況下,這個(gè)路徑是相對(duì)于調(diào)用模塊的目錄的;但是如果指定了 package 參數(shù),那么它就是相對(duì)于該包的。 為了保證操作系統(tǒng)的獨(dú)立性,每個(gè)文件名都應(yīng)該使用字符來(lái)分隔路徑段,并且不能是絕對(duì)路徑(即不能以 / 開(kāi)始)。

  • 如果 module_relativeFalse ,那么 paths 中的每個(gè)文件名都指定了一個(gè)操作系統(tǒng)特定的路徑。 路徑可以是絕對(duì)的,也可以是相對(duì)的;相對(duì)路徑是關(guān)于當(dāng)前工作目錄的解析。

可選參數(shù) package 是一個(gè)Python包或一個(gè)Python包的名字,其目錄應(yīng)該被用作 paths 中模塊相關(guān)文件名的基本目錄。 如果沒(méi)有指定包,那么調(diào)用模塊的目錄將作為模塊相關(guān)文件名的基礎(chǔ)目錄。 如果 module_relativeFalse ,那么指定 package 是錯(cuò)誤的。

可選的參數(shù) setUp 為測(cè)試套件指定了一個(gè)設(shè)置函數(shù)。在運(yùn)行每個(gè)文件中的測(cè)試之前,它被調(diào)用。 setUp 函數(shù)將被傳遞給一個(gè) DocTest 對(duì)象。 setUp函數(shù)可以通過(guò)測(cè)試的 globs 屬性訪問(wèn)測(cè)試的全局變量。

可選的參數(shù) tearDown 指定了測(cè)試套件的卸載函數(shù)。 在運(yùn)行每個(gè)文件中的測(cè)試后,它會(huì)被調(diào)用。 tearDown 函數(shù)將被傳遞一個(gè) DocTest 對(duì)象。 setUp函數(shù)可以通過(guò)測(cè)試的 globs 屬性訪問(wèn)測(cè)試的全局變量。

可選參數(shù) globs 是一個(gè)包含測(cè)試的初始全局變量的字典。 這個(gè)字典的一個(gè)新副本為每個(gè)測(cè)試創(chuàng)建。 默認(rèn)情況下, globs 是一個(gè)新的空字典。

可選參數(shù) optionflags 為測(cè)試指定默認(rèn)的doctest選項(xiàng),通過(guò)將各個(gè)選項(xiàng)的標(biāo)志連接在一起創(chuàng)建。 參見(jiàn)章節(jié) 選項(xiàng)標(biāo)記 。參見(jiàn)下面的函數(shù) set_unittest_reportflags() ,以了解設(shè)置報(bào)告選項(xiàng)的更好方法。

可選參數(shù) parser 指定一個(gè) DocTestParser (或子類(lèi)),它應(yīng)該被用來(lái)從文件中提取測(cè)試。它默認(rèn)為一個(gè)普通的解析器(即``DocTestParser()``)。

可選參數(shù) encoding 指定了一個(gè)編碼,應(yīng)該用來(lái)將文件轉(zhuǎn)換為unicode。

該全局 __file__ 被添加到提供給用 DocFileSuite() 從文本文件加載的doctest的全局變量中。

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

將一個(gè)模塊的doctest測(cè)試轉(zhuǎn)換為 unittest.TestSuite 。

返回的 unittest.TestSuite 將由 unittest 框架運(yùn)行,并運(yùn)行模塊中的每個(gè) doctest。 如果任何一個(gè)測(cè)試失敗,那么合成的單元測(cè)試就會(huì)失敗,并觸發(fā)一個(gè) failureException 異常,顯示包含該測(cè)試的文件名和一個(gè)(有時(shí)是近似的)行號(hào)。

可選參數(shù) module 提供了要測(cè)試的模塊。 它可以是一個(gè)模塊對(duì)象或一個(gè)(可能是帶點(diǎn)的)模塊名稱。 如果沒(méi)有指定,則使用調(diào)用此函數(shù)的模塊。

可選參數(shù) globs 是一個(gè)包含測(cè)試的初始全局變量的字典。 這個(gè)字典的一個(gè)新副本為每個(gè)測(cè)試創(chuàng)建。 默認(rèn)情況下, globs 是一個(gè)新的空字典。

可選參數(shù) extraglobs 指定了一組額外的全局變量,這些變量被合并到 globs 中。 默認(rèn)情況下,不使用額外的全局變量。

可選參數(shù) test_finderDocTestFinder 對(duì)象(或一個(gè)可替換的對(duì)象),用于從模塊中提取測(cè)試。

可選參數(shù) setUp 、 tearDownoptionflags 與上述函數(shù) DocFileSuite() 相同。

這個(gè)函數(shù)使用與 testmod() 相同的搜索技術(shù)。

在 3.5 版更改: 如果 module 不包含任何文件串,則 DocTestSuite() 返回一個(gè)空的 unittest.TestSuite,而不是觸發(fā) ValueError。

DocTestSuite()doctest.DocTestCase 實(shí)例中創(chuàng)建一個(gè) unittest.TestSuite ,而 DocTestCaseunittest.TestCase 的子類(lèi)。 DocTestCase 在這里沒(méi)有記錄(這是一個(gè)內(nèi)部細(xì)節(jié)),但研究其代碼可以回答關(guān)于 unittest 集成的實(shí)際細(xì)節(jié)問(wèn)題。

同樣, DocFileSuite()doctest.DocFileCase 實(shí)例中創(chuàng)建一個(gè) unittest.TestSuite ,并且 DocFileCaseDocTestCase 的一個(gè)子類(lèi)。

所以這兩種創(chuàng)建 unittest.TestSuite 的方式都是運(yùn)行 DocTestCase 的實(shí)例。這一點(diǎn)很重要,因?yàn)橛幸粋€(gè)微妙的原因:當(dāng)你自己運(yùn)行 doctest 函數(shù)時(shí),你可以直接控制使用中的 doctest 選項(xiàng),通過(guò)傳遞選項(xiàng)標(biāo)志給 doctest 函數(shù)。 然而,如果你正在編寫(xiě)一個(gè) unittest 框架, unittest 最終會(huì)控制測(cè)試的運(yùn)行時(shí)間和方式。 框架作者通常希望控制 doctest 的報(bào)告選項(xiàng)(也許,例如,由命令行選項(xiàng)指定),但沒(méi)有辦法通過(guò) unittestdoctest 測(cè)試運(yùn)行者傳遞選項(xiàng)。

出于這個(gè)原因, doctest 也支持一個(gè)概念,即 doctest 報(bào)告特定于 unittest 支持的標(biāo)志,通過(guò)這個(gè)函數(shù):

doctest.set_unittest_reportflags(flags)?

設(shè)置要使用的 doctest 報(bào)告標(biāo)志。

參數(shù) flags 是選項(xiàng)標(biāo)志的 bitwise OR 。 參見(jiàn)章節(jié) 選項(xiàng)標(biāo)記 。 只有 "報(bào)告標(biāo)志" 可以被使用。

這是一個(gè)模塊的全局設(shè)置,并影響所有未來(lái)由模塊 unittest 運(yùn)行的測(cè)試: DocTestCaserunTest() 方法會(huì)查看 DocTestCase 實(shí)例構(gòu)建時(shí)為測(cè)試用例指定的選項(xiàng)標(biāo)志。 如果沒(méi)有指定報(bào)告標(biāo)志(這是典型的和預(yù)期的情況), doctestunittest 報(bào)告標(biāo)志被 bitwise ORed 到選項(xiàng)標(biāo)志中,這樣增加的選項(xiàng)標(biāo)志被傳遞給 DocTestRunner 實(shí)例來(lái)運(yùn)行doctest。 如果在構(gòu)建 DocTestCase 實(shí)例時(shí)指定了任何報(bào)告標(biāo)志,那么 doctestunittest 報(bào)告標(biāo)志會(huì)被忽略。

unittest 報(bào)告標(biāo)志的值在調(diào)用該函數(shù)之前是有效的,由該函數(shù)返回。

高級(jí) API?

基本 API 是一個(gè)簡(jiǎn)單的封裝,旨在使 doctest 易于使用。它相當(dāng)靈活,應(yīng)該能滿足大多數(shù)用戶的需求;但是,如果你需要對(duì)測(cè)試進(jìn)行更精細(xì)的控制,或者希望擴(kuò)展 doctest 的功能,那么你應(yīng)該使用高級(jí) API 。

高級(jí)API圍繞著兩個(gè)容器類(lèi),用于存儲(chǔ)從 doctest 案例中提取的交互式用例:

  • Example: 一個(gè)單一的 Python statement ,與它的預(yù)期輸出配對(duì)。

  • DocTest: 一組 Examples 的集合,通常從一個(gè)文檔字符串或文本文件中提取。

定義了額外的處理類(lèi)來(lái)尋找、解析和運(yùn)行,并檢查 doctest 的用例。

  • DocTestFinder : 查找給定模塊中的所有文檔串,并使用 DocTestParser 從每個(gè)包含交互式用例的文檔串中創(chuàng)建一個(gè) DocTest 。

  • DocTestParser : 從一個(gè)字符串(如一個(gè)對(duì)象的文檔串)創(chuàng)建一個(gè) DocTest 對(duì)象。

  • DocTestRunner : 執(zhí)行 DocTest 中的用例,并使用 OutputChecker 來(lái)驗(yàn)證其輸出。

  • OutputChecker : 將一個(gè)測(cè)試用例的實(shí)際輸出與預(yù)期輸出進(jìn)行比較,并決定它們是否匹配。

這些處理類(lèi)之間的關(guān)系總結(jié)在下圖中:

                            list of:
+------+                   +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+    |        ^     +---------+     |       ^    (printed)
            |        |     | Example |     |       |
            v        |     |   ...   |     v       |
           DocTestParser   | Example |   OutputChecker
                           +---------+

DocTest 對(duì)象?

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

應(yīng)該在單一命名空間中運(yùn)行的doctest用例的集合。構(gòu)造函數(shù)參數(shù)被用來(lái)初始化相同名稱的屬性。

DocTest 定義了以下屬性。 它們由構(gòu)造函數(shù)初始化,不應(yīng)該被直接修改。

examples?

一個(gè) Example 對(duì)象的列表,它編碼了應(yīng)該由該測(cè)試運(yùn)行的單個(gè)交互式 Python 用例。

globs?

例子應(yīng)該運(yùn)行的命名空間(又稱 globals )。這是一個(gè)將名字映射到數(shù)值的字典。例子對(duì)名字空間的任何改變(比如綁定新的變量)將在測(cè)試運(yùn)行后反映在 globs 中。

name?

識(shí)別 DocTest 的字符串名稱。 通常情況下,這是從測(cè)試中提取的對(duì)象或文件的名稱。

filename?

這個(gè) DocTest 被提取的文件名;或者為``None``,如果文件名未知,或者 DocTest 沒(méi)有從文件中提取。

lineno?

filename 中的行號(hào),這個(gè) DocTest 開(kāi)始的地方,或者行號(hào)不可用時(shí)為``None``。 這個(gè)行號(hào)相對(duì)于文件的開(kāi)頭來(lái)說(shuō)是零的。

docstring?

從測(cè)試中提取的字符串,或者如果字符串不可用,或者為 None ,如果測(cè)試沒(méi)有從字符串中提取。

Example 對(duì)象?

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

單個(gè)交互式用例,由一個(gè) Python 語(yǔ)句及其預(yù)期輸出組成。 構(gòu)造函數(shù)參數(shù)被用來(lái)初始化相同名稱的屬性。

Example 定義了以下屬性。 它們由構(gòu)造函數(shù)初始化,不應(yīng)該被直接修改。

source?

一個(gè)包含該用例源碼的字符串。 源碼由一個(gè) Python 語(yǔ)句組成,并且總是以換行結(jié)束;構(gòu)造函數(shù)在必要時(shí)添加一個(gè)換行。

want?

運(yùn)行這個(gè)用例的源碼的預(yù)期輸出(可以是 stdout ,也可以是異常情況下的回溯)。 want 以一個(gè)換行符結(jié)束,除非沒(méi)有預(yù)期的輸出,在這種情況下它是一個(gè)空字符串。 構(gòu)造函數(shù)在必要時(shí)添加一個(gè)換行。

exc_msg?

用例產(chǎn)生的異常信息,如果這個(gè)例子被期望產(chǎn)生一個(gè)異常;或者為 None ,如果它不被期望產(chǎn)生一個(gè)異常。 這個(gè)異常信息與 traceback.format_exception_only() 的返回值進(jìn)行比較。 exc_msg 以換行結(jié)束,除非是 None

lineno?

包含本例的字符串中的行號(hào),即本例的開(kāi)始。 這個(gè)行號(hào)相對(duì)于包含字符串的開(kāi)頭來(lái)說(shuō)是以零開(kāi)始的。

indent?

用例在包含字符串中的縮進(jìn),即在用例的第一個(gè)提示前有多少個(gè)空格字符。

options?

一個(gè)從選項(xiàng)標(biāo)志到 TrueFalse 的字典映射,用于覆蓋這個(gè)例子的默認(rèn)選項(xiàng)。 任何不包含在這個(gè)字典中的選項(xiàng)標(biāo)志都被保留為默認(rèn)值(由 DocTestRunneroptionflags 指定)。默認(rèn)情況下,沒(méi)有選項(xiàng)被設(shè)置。

DocTestFinder 對(duì)象?

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

一個(gè)處理類(lèi),用于從一個(gè)給定的對(duì)象的 docstring 和其包含的對(duì)象的 docstring 中提取與之相關(guān)的 DocTest 。 DocTest 可以從模塊、類(lèi)、函數(shù)、方法、靜態(tài)方法、類(lèi)方法和屬性中提取。

可選的參數(shù) verbose 可以用來(lái)顯示查找器搜索到的對(duì)象。 它的默認(rèn)值是 False (無(wú)輸出)。

可選的參數(shù) parser 指定了 DocTestParser 對(duì)象(或一個(gè)可替換的對(duì)象),用于從文檔串中提取 doctest 。

如果可選的參數(shù) recurse 是 False ,那么 DocTestFinder.find() 將只檢查給定的對(duì)象,而不是任何包含的對(duì)象。

如果可選參數(shù) exclude_empty 為 False ,那么 DocTestFinder.find() 將包括對(duì)文檔字符串為空的對(duì)象的測(cè)試。

DocTestFinder 定義了以下方法:

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

返回 DocTest 的列表,該列表由 obj 的文檔串或其包含的任何對(duì)象的文檔串定義。

可選參數(shù) name 指定了對(duì)象的名稱;這個(gè)名稱將被用來(lái)為返回的 DocTest 構(gòu)建名稱。 如果沒(méi)有指定*name*,則使用 obj.__name__ 。

可選參數(shù) module 是包含給定對(duì)象的模塊。如果沒(méi)有指定模塊或者是 None ,那么測(cè)試查找器將試圖自動(dòng)確定正確的模塊。 該對(duì)象被使用的模塊:

  • 作為一個(gè)默認(rèn)的命名空間,如果沒(méi)有指定 globs 。

  • 為了防止 DocTestFinder 從其他模塊導(dǎo)入的對(duì)象中提取 DocTest 。 (包含有除 module 以外的模塊的對(duì)象會(huì)被忽略)。

  • 找到包含該對(duì)象的文件名。

  • 找到該對(duì)象在其文件中的行號(hào)。

如果 moduleFalse ,將不會(huì)試圖找到這個(gè)模塊。 這是不明確的,主要用于測(cè)試 doctest 本身:如果 moduleFalse ,或者是 None 但不能自動(dòng)找到,那么所有對(duì)象都被認(rèn)為屬于(不存在的)模塊,所以所有包含的對(duì)象將(遞歸地)被搜索到 doctest 。

每個(gè) DocTest 的 globals 是由 globsextraglobs 組合而成的( extraglobs 的綁定覆蓋 globs 的綁定)。 為每個(gè) DocTest 創(chuàng)建一個(gè)新的 globals 字典的淺層拷貝。如果沒(méi)有指定 globs ,那么它默認(rèn)為模塊的 __dict__ ,如果指定了或者為 {} ,則除外。 如果沒(méi)有指定 extraglobs ,那么它默認(rèn)為 {} 。

DocTestParser 對(duì)象?

class doctest.DocTestParser?

一個(gè)處理類(lèi),用于從一個(gè)字符串中提取交互式的用例,并使用它們來(lái)創(chuàng)建一個(gè) DocTest 對(duì)象。

DocTestParser 定義了以下方法:

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

從給定的字符串中提取所有的測(cè)試用例,并將它們收集到一個(gè) DocTest 對(duì)象中。

globsname 、 filenamelineno 是新的 DocTest 對(duì)象的屬性。 更多信息請(qǐng)參見(jiàn) DocTest 的文檔。

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

從給定的字符串中提取所有的測(cè)試用例,并以 Example 對(duì)象列表的形式返回。 行數(shù)以 0 為基數(shù)。 可選參數(shù) name 用于識(shí)別這個(gè)字符串的名稱,只用于錯(cuò)誤信息。

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

將給定的字符串分成用例和中間的文本,并以 Example 和字符串交替的列表形式返回。 Example 的行號(hào)以 0 為基數(shù)。 可選參數(shù) name 用于識(shí)別這個(gè)字符串的名稱,只用于錯(cuò)誤信息。

DocTestRunner 對(duì)象?

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

一個(gè)處理類(lèi),用于執(zhí)行和驗(yàn)證 DocTest 中的交互式用例。

預(yù)期輸出和實(shí)際輸出之間的比較是由一個(gè) OutputChecker 完成的。 這種比較可以用一些選項(xiàng)標(biāo)志來(lái)定制;更多信息請(qǐng)看 選項(xiàng)標(biāo)記 部分。 如果選項(xiàng)標(biāo)志不夠,那么也可以通過(guò)向構(gòu)造函數(shù)傳遞 OutputChecker 的子類(lèi)來(lái)定制比較。

測(cè)試運(yùn)行器的顯示輸出可以通過(guò)兩種方式控制。首先,一個(gè)輸出函數(shù)可以被傳遞給 TestRunner.run() ;這個(gè)函數(shù)將被調(diào)用,并顯示出應(yīng)該顯示的字符串。 它的默認(rèn)值是 sys.stdout.write 。 如果捕獲輸出是不夠的,那么也可以通過(guò)子類(lèi)化 DocTestRunner 來(lái)定制顯示輸出,并重寫(xiě) report_start() , report_success() , report_unexpected_exception()report_failure() 方法。

可選的關(guān)鍵字參數(shù) checker 指定了 OutputChecker 對(duì)象(或其相似替換),它應(yīng)該被用來(lái)比較預(yù)期輸出和 doctest 用例的實(shí)際輸出。

可選的關(guān)鍵字參數(shù) verbose 控制 DocTestRunner 的詳細(xì)程度。 如果 verboseTrue ,那么每個(gè)用例的信息都會(huì)被打印出來(lái),當(dāng)它正在運(yùn)行時(shí)。 如果 verboseFalse ,則只打印失敗的信息。 當(dāng) verbose 沒(méi)有指定,或者為 None ,如果使用了命令行開(kāi)關(guān) -v ,則使用verbose輸出。

可選的關(guān)鍵字參數(shù) optionflags 可以用來(lái)控制測(cè)試運(yùn)行器如何比較預(yù)期輸出和實(shí)際輸出,以及如何顯示失敗。更多信息,請(qǐng)參見(jiàn)章節(jié) 選項(xiàng)標(biāo)記 。

DocTestParser 定義了以下方法:

report_start(out, test, example)?

報(bào)告測(cè)試運(yùn)行器即將處理給定的用例。提供這個(gè)方法是為了讓 DocTestRunner 的子類(lèi)能夠定制他們的輸出;它不應(yīng)該被直接調(diào)用。

example 是即將被處理的用。 test包含用例 的測(cè)試。 out 是傳遞給 DocTestRunner.run() 的輸出函數(shù)。

report_success(out, test, example, got)?

報(bào)告給定的用例運(yùn)行成功。 提供這個(gè)方法是為了讓 DocTestRunner 的子類(lèi)能夠定制他們的輸出;它不應(yīng)該被直接調(diào)用。

example 是即將被處理的用例。 got 是這個(gè)例子的實(shí)際輸出。 test 是包含 example 的測(cè)試。 out 是傳遞給 DocTestRunner.run() 的輸出函數(shù)。

report_failure(out, test, example, got)?

報(bào)告給定的用例運(yùn)行失敗。 提供這個(gè)方法是為了讓 DocTestRunner 的子類(lèi)能夠定制他們的輸出;它不應(yīng)該被直接調(diào)用。

example 是即將被處理的用例。 got 是這個(gè)例子的實(shí)際輸出。 test 是包含 example 的測(cè)試。 out 是傳遞給 DocTestRunner.run() 的輸出函數(shù)。

report_unexpected_exception(out, test, example, exc_info)?

報(bào)告給定的用例觸發(fā)了一個(gè)異常。 提供這個(gè)方法是為了讓 DocTestRunner 的子類(lèi)能夠定制他們的輸出;它不應(yīng)該被直接調(diào)用。

example 是將要被處理的用例。 exc_info 是一個(gè)元組,包含關(guān)于異常的信息(如由 sys.exc_info() 返回)。 test 是包含 example 的測(cè)試。 out 是傳遞給 DocTestRunner.run() 的輸出函數(shù)。

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

test (一個(gè) DocTest 對(duì)象)中運(yùn)行這些用例,并使用寫(xiě)入函數(shù) out 顯示結(jié)果。

這些用例都是在命名空間 test.globs 中運(yùn)行的。 如果 clear_globs 為 True (默認(rèn)),那么這個(gè)命名空間將在測(cè)試運(yùn)行后被清除,以幫助進(jìn)行垃圾回收。如果你想在測(cè)試完成后檢查命名空間,那么使用 clear_globs=False 。

compileflags 給出了 Python 編譯器在運(yùn)行例子時(shí)應(yīng)該使用的標(biāo)志集。如果沒(méi)有指定,那么它將默認(rèn)為適用于 globs 的 future-import 標(biāo)志集。

每個(gè)用例的輸出都使用 DocTestRunner 的輸出檢查器進(jìn)行檢查,結(jié)果由 DocTestRunner.report_*() 方法進(jìn)行格式化。

summarize(verbose=None)?

打印這個(gè) DocTestRunner 運(yùn)行過(guò)的所有測(cè)試用例的摘要,并返回一個(gè) named tuple TestResults(failed, attempted) 。

可選的 verbose 參數(shù)控制摘要的詳細(xì)程度。 如果沒(méi)有指定 verbose ,那么將使用 DocTestRunner 的 verbose 。

OutputChecker 對(duì)象?

class doctest.OutputChecker?

一個(gè)用于檢查測(cè)試用例的實(shí)際輸出是否與預(yù)期輸出相匹配的類(lèi)。 OutputChecker 定義了兩個(gè)方法: check_output() ,比較給定的一對(duì)輸出,如果它們匹配則返回 True ; output_difference() ,返回一個(gè)描述兩個(gè)輸出之間差異的字符串。

OutputChecker 定義了以下方法:

check_output(want, got, optionflags)?

如果一個(gè)用例的實(shí)際輸出( got )與預(yù)期輸出( want )匹配,則返回 True 。 如果這些字符串是相同的,總是被認(rèn)為是匹配的;但是根據(jù)測(cè)試運(yùn)行器使用的選項(xiàng)標(biāo)志,也可能有幾種非精確的匹配類(lèi)型。 參見(jiàn)章節(jié) 選項(xiàng)標(biāo)記 了解更多關(guān)于選項(xiàng)標(biāo)志的信息。

output_difference(example, got, optionflags)?

返回一個(gè)字符串,描述給定用例( example )的預(yù)期輸出和實(shí)際輸出( got )之間的差異。 optionflags 是用于比較 wantgot 的選項(xiàng)標(biāo)志集。

調(diào)試?

Doctest 提供了幾種調(diào)試 doctest 用例的機(jī)制:

  • 有幾個(gè)函數(shù)將測(cè)試轉(zhuǎn)換為可執(zhí)行的 Python 程序,這些程序可以在 Python 調(diào)試器, pdb 下運(yùn)行。

  • DebugRunner 類(lèi)是 DocTestRunner 的一個(gè)子類(lèi),它為第一個(gè)失敗的用例觸發(fā)一個(gè)異常,包含關(guān)于這個(gè)用例的信息。這些信息可以用來(lái)對(duì)這個(gè)用例進(jìn)行事后調(diào)試。

  • DocTestSuite() 生成的 unittest 用例支持由 unittest.TestCase 定義的 debug() 方法,。

  • 你可以在 doctest 的用例中加入對(duì) pdb.set_trace() 的調(diào)用,當(dāng)這一行被執(zhí)行時(shí),你會(huì)進(jìn)入 Python 調(diào)試器。 然后你可以檢查變量的當(dāng)前值,等等。 例如,假設(shè) a.py 只包含這個(gè)模塊 docstring

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

    那么一個(gè)交互式Python會(huì)話可能是這樣的:

    >>>
    >>> 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)
    >>>
    

將測(cè)試轉(zhuǎn)換為 Python 代碼的函數(shù),并可能在調(diào)試器下運(yùn)行合成的代碼:

doctest.script_from_examples(s)?

將帶有用例的文本轉(zhuǎn)換為腳本。

參數(shù) s 是一個(gè)包含測(cè)試用例的字符串。 該字符串被轉(zhuǎn)換為 Python 腳本,其中 s 中的 doctest 用例被轉(zhuǎn)換為常規(guī)代碼,其他的都被轉(zhuǎn)換為 Python 注釋。 生成的腳本將以字符串的形式返回。例如,

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

這個(gè)函數(shù)在內(nèi)部被其他函數(shù)使用(見(jiàn)下文),但當(dāng)你想把一個(gè)交互式 Python 會(huì)話轉(zhuǎn)化為 Python 腳本時(shí),也會(huì)很有用。

doctest.testsource(module, name)?

將一個(gè)對(duì)象的 doctest 轉(zhuǎn)換為一個(gè)腳本。

參數(shù) module 是一個(gè)模塊對(duì)象,或者一個(gè)模塊帶點(diǎn)的名稱,包含對(duì)其 doctest 感興趣的對(duì)象。 參數(shù) name 是具有感興趣的測(cè)試的對(duì)象的名稱(在模塊中)。 結(jié)果是一個(gè)字符串,包含該對(duì)象的文本串轉(zhuǎn)換為 Python 腳本,如上面 script_from_examples() 所述。 例如,如果模塊 a.py 包含一個(gè)頂級(jí)函數(shù) f() ,那么

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

打印函數(shù) f() 的文檔串的腳本版本,將測(cè)試轉(zhuǎn)換為代碼,其余部分放在注釋中。

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

對(duì)一個(gè)對(duì)象的 doctest 進(jìn)行調(diào)試。

modulename 參數(shù)與上面函數(shù) testsource() 的參數(shù)相同。 被命名對(duì)象的文本串的合成 Python 腳本被寫(xiě)入一個(gè)臨時(shí)文件,然后該文件在 Python 調(diào)試器 pdb 的控制下運(yùn)行。

module.__dict__ 的一個(gè)淺層拷貝被用于本地和全局的執(zhí)行環(huán)境。

可選參數(shù) pm 控制是否使用事后調(diào)試。 如果 pm 為 True ,則直接運(yùn)行腳本文件,只有當(dāng)腳本通過(guò)引發(fā)一個(gè)未處理的異常而終止時(shí),調(diào)試器才會(huì)介入。如果是這樣,就會(huì)通過(guò) pdb.post_mortem() 調(diào)用事后調(diào)試,并傳遞未處理異常的跟蹤對(duì)象。如果沒(méi)有指定 pm ,或者是 False ,腳本將從一開(kāi)始就在調(diào)試器下運(yùn)行,通過(guò)傳遞一個(gè)適當(dāng)?shù)?exec() 調(diào)用給 pdb.run()

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

在一個(gè)字符串中調(diào)試 doctest 。

這就像上面的函數(shù) debug() ,只是通過(guò) src 參數(shù),直接指定一個(gè)包含測(cè)試用例的字符串。

可選參數(shù) pm 的含義與上述函數(shù) debug() 的含義相同。

可選的參數(shù) globs 給出了一個(gè)字典,作為本地和全局的執(zhí)行環(huán)境。 如果沒(méi)有指定,或者為 None ,則使用一個(gè)空的字典。如果指定,則使用字典的淺層拷貝。

DebugRunner 類(lèi),以及它可能觸發(fā)的特殊異常,是測(cè)試框架作者最感興趣的,在此僅作簡(jiǎn)要介紹。請(qǐng)看源代碼,特別是 DebugRunner 的文檔串(這是一個(gè)測(cè)試?。┮粤私飧嗉?xì)節(jié)。

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

DocTestRunner 的一個(gè)子類(lèi),一旦遇到失敗,就會(huì)觸發(fā)一個(gè)異常。 如果一個(gè)意外的異常發(fā)生,就會(huì)引發(fā)一個(gè) UnexpectedException 異常,包含測(cè)試、用例和原始異常。 如果輸出不匹配,那么就會(huì)引發(fā)一個(gè) DocTestFailure 異常,包含測(cè)試、用例和實(shí)際輸出。

關(guān)于構(gòu)造函數(shù)參數(shù)和方法的信息,請(qǐng)參見(jiàn) DocTestRunner 部分的文檔 高級(jí) API 。

DebugRunner 實(shí)例可能會(huì)觸發(fā)兩種異常。

exception doctest.DocTestFailure(test, example, got)?

DocTestRunner 觸發(fā)的異常,表示一個(gè) doctest 用例的實(shí)際輸出與預(yù)期輸出不一致。構(gòu)造函數(shù)參數(shù)被用來(lái)初始化相同名稱的屬性。

DocTestFailure 定義了以下屬性:

DocTestFailure.test?

當(dāng)該用例失敗時(shí)正在運(yùn)行的 DocTest 對(duì)象。

DocTestFailure.example?

失敗的 Example 。

DocTestFailure.got?

用例的實(shí)際輸出。

exception doctest.UnexpectedException(test, example, exc_info)?

一個(gè)由 DocTestRunner 觸發(fā)的異常,以提示一個(gè) doctest 用例引發(fā)了一個(gè)意外的異常。 構(gòu)造函數(shù)參數(shù)被用來(lái)初始化相同名稱的屬性。

UnexpectedException 定義了以下屬性:

UnexpectedException.test?

當(dāng)該用例失敗時(shí)正在運(yùn)行的 DocTest 對(duì)象。

UnexpectedException.example?

失敗的 Example 。

UnexpectedException.exc_info?

一個(gè)包含意外異常信息的元組,由 sys.ex_info() 返回。

肥皂盒?

正如介紹中提到的, doctest 已經(jīng)發(fā)展到有三個(gè)主要用途:

  1. 檢查 docstring 中的用例。

  2. 回歸測(cè)試。

  3. 可執(zhí)行的文檔/文字測(cè)試。

這些用途有不同的要求,區(qū)分它們是很重要的。特別是,用晦澀難懂的測(cè)試用例來(lái)填充你的文檔字符串會(huì)使文檔變得很糟糕。

在編寫(xiě)文檔串時(shí),要謹(jǐn)慎地選擇文檔串的用例。這是一門(mén)需要學(xué)習(xí)的藝術(shù)——一開(kāi)始可能并不自然。用例應(yīng)該為文檔增加真正的價(jià)值。 一個(gè)好的用例往往可以抵得上許多文字。如果用心去做,這些用例對(duì)你的用戶來(lái)說(shuō)是非常有價(jià)值的,而且隨著時(shí)間的推移和事情的變化,收集這些用例所花費(fèi)的時(shí)間也會(huì)得到很多倍的回報(bào)。 我仍然驚訝于我的 doctest 用例在一個(gè)“無(wú)害”的變化后停止工作。

Doctest 也是回歸測(cè)試的一個(gè)很好的工具,特別是如果你不吝嗇解釋的文字。 通過(guò)交錯(cuò)的文本和用例,可以更容易地跟蹤真正的測(cè)試,以及為什么。當(dāng)測(cè)試失敗時(shí),好的文本可以使你更容易弄清問(wèn)題所在,以及如何解決。 的確,你可以在基于代碼的測(cè)試中寫(xiě)大量的注釋,但很少有程序員這樣做。許多人發(fā)現(xiàn),使用 doctest 方法反而能使測(cè)試更加清晰。 也許這只是因?yàn)?doctest 使寫(xiě)散文比寫(xiě)代碼容易一些,而在代碼中寫(xiě)注釋則有點(diǎn)困難。 我認(rèn)為它比這更深入:當(dāng)寫(xiě)一個(gè)基于 doctest 的測(cè)試時(shí),自然的態(tài)度是你想解釋你的軟件的細(xì)微之處,并用例子來(lái)說(shuō)明它們。這反過(guò)來(lái)又自然地導(dǎo)致了測(cè)試文件從最簡(jiǎn)單的功能開(kāi)始,然后邏輯地發(fā)展到復(fù)雜和邊緣案例。 一個(gè)連貫的敘述是結(jié)果,而不是一個(gè)孤立的函數(shù)集合,似乎是隨機(jī)的測(cè)試孤立的功能位。 這是一種不同的態(tài)度,產(chǎn)生不同的結(jié)果,模糊了測(cè)試和解釋之間的區(qū)別。

回歸測(cè)試最好限制在專用對(duì)象或文件中。 有幾種組織測(cè)試的選擇:

  • 編寫(xiě)包含測(cè)試案例的文本文件作為交互式例子,并使用 testfile()DocFileSuite() 來(lái)測(cè)試這些文件。 建議這樣做,盡管對(duì)于新的項(xiàng)目來(lái)說(shuō)是最容易做到的,從一開(kāi)始就設(shè)計(jì)成使用 doctest 。

  • 定義命名為 _regrtest_topic 的函數(shù),由單個(gè)文檔串組成,包含命名主題的測(cè)試用例。 這些函數(shù)可以包含在與模塊相同的文件中,或分離出來(lái)成為一個(gè)單獨(dú)的測(cè)試文件。

  • 定義一個(gè)從回歸測(cè)試主題到包含測(cè)試用例的文檔串的 __test__ 字典映射。

當(dāng)你把你的測(cè)試放在一個(gè)模塊中時(shí),這個(gè)模塊本身就可以成為測(cè)試運(yùn)行器。 當(dāng)一個(gè)測(cè)試失敗時(shí),你可以安排你的測(cè)試運(yùn)行器只重新運(yùn)行失敗的測(cè)試,同時(shí)調(diào)試問(wèn)題。 下面是這樣一個(gè)測(cè)試運(yùn)行器的最小例子:

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("{} failures out of {} tests".format(fail, total))

備注

1

不支持同時(shí)包含預(yù)期輸出和異常的用例。試圖猜測(cè)一個(gè)在哪里結(jié)束,另一個(gè)在哪里開(kāi)始,太容易出錯(cuò)了,而且這也會(huì)使測(cè)試變得混亂。