如何執行 doctest¶
預設情況下,所有符合 test*.txt
樣式的檔案都將透過 python 標準 doctest
模組執行。您可以透過在命令列中下達
pytest --doctest-glob="*.rst"
來變更樣式。可以在命令列中多次提供 --doctest-glob
。
如果您有一個像這樣的文字檔
# content of test_example.txt
hello this is a doctest
>>> x = 3
>>> x
3
那麼您可以直接呼叫 pytest
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item
test_example.txt . [100%]
============================ 1 passed in 0.12s =============================
預設情況下,pytest 會收集 test*.txt
檔案,尋找 doctest 指令,但您可以使用 --doctest-glob
選項 (允許多個) 傳遞其他 glob。
除了文字檔之外,您也可以直接從類別和函式的文件字串中執行 doctest,包括從測試模組中執行
# content of mymodule.py
def something():
"""a doctest in a docstring
>>> something()
42
"""
return 42
$ pytest --doctest-modules
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items
mymodule.py . [ 50%]
test_example.txt . [100%]
============================ 2 passed in 0.12s =============================
您可以將這些變更永久放在專案中,方法是將它們放入類似這樣的 pytest.ini 檔案中
# content of pytest.ini
[pytest]
addopts = --doctest-modules
編碼¶
預設編碼為 UTF-8,但您可以使用 doctest_encoding
ini 選項指定將用於那些 doctest 檔案的編碼
# content of pytest.ini
[pytest]
doctest_encoding = latin1
使用「doctest」選項¶
Python 的標準 doctest
模組提供了一些 選項 來設定 doctest 測試的嚴謹性。在 pytest 中,您可以使用設定檔啟用這些旗標。
例如,若要讓 pytest 忽略尾隨空白和忽略冗長的例外堆疊追蹤,您可以寫
[pytest]
doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
或者,可以在文件測試本身的內嵌註解中啟用選項
>>> something_that_raises() # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: ...
pytest 也引入了新的選項
ALLOW_UNICODE
:啟用時,會從預期的 doctest 輸出中移除 unicode 字串中的u
前綴。這允許 doctest 在 Python 2 和 Python 3 中不變地執行。ALLOW_BYTES
:類似地,會從預期的 doctest 輸出中移除位元組字串中的b
前綴。NUMBER
:啟用時,浮點數只需與您在預期的 doctest 輸出中寫入的精度相符即可。這些數字使用pytest.approx()
進行比較,相對容差等於精度。例如,在將3.14
與pytest.approx(math.pi, rel=10**-2)
進行比較時,以下輸出只需與 2 個小數位相符即可>>> math.pi 3.14
如果您寫入
3.1416
,則實際輸出需要與大約 4 個小數位相符;以此類推。這避免了由有限浮點精確度造成的誤判,如下所示
Expected: 0.233 Got: 0.23300000000000001
NUMBER
也支援浮點數清單,事實上,它會比對輸出中任何地方出現的浮點數,即使在字串內部也是如此!這表示在組態檔中於doctest_optionflags
中啟用它可能不適當。已在版本 5.1 中新增。
在失敗時繼續¶
預設情況下,pytest 僅會針對特定 doctest 報告第一個失敗。如果您想在有失敗時繼續測試,請執行
pytest --doctest-modules --doctest-continue-on-failure
輸出格式¶
您可以使用選項中的標準 doctest 模組格式來變更 doctest 失敗時的 diff 輸出格式(請參閱 doctest.REPORT_UDIFF
、doctest.REPORT_CDIFF
、doctest.REPORT_NDIFF
、doctest.REPORT_ONLY_FIRST_FAILURE
)
pytest --doctest-modules --doctest-report none
pytest --doctest-modules --doctest-report udiff
pytest --doctest-modules --doctest-report cdiff
pytest --doctest-modules --doctest-report ndiff
pytest --doctest-modules --doctest-report only_first_failure
pytest 特定功能¶
提供了一些功能,讓撰寫 doctest 變得更容易,或與現有的測試套件有更好的整合。但請記住,使用這些功能會讓您的 doctest 與標準 doctests
模組不相容。
使用 fixtures¶
可以使用 getfixture
輔助程式使用 fixtures
# content of example.rst
>>> tmp = getfixture('tmp_path')
>>> ...
>>>
請注意,fixture 需要定義在 pytest 可見的地方,例如 conftest.py
檔案或外掛程式;除非由 python_files
明確設定,否則通常不會掃描包含文件字串的正常 Python 檔案以尋找 fixtures。
此外,在執行文字 doctest 檔案時,支援 usefixtures 標記和標記為 autouse 的 fixtures。
「doctest_namespace」固定裝置¶
可以使用 doctest_namespace
固定裝置將項目注入到 doctest 執行的命名空間中。它旨在用於你自己的固定裝置中,以提供使用它們的測試環境。
doctest_namespace
是標準的 dict
物件,你可以將想要顯示在 doctest 命名空間中的物件放入其中
# content of conftest.py
import pytest
import numpy
@pytest.fixture(autouse=True)
def add_np(doctest_namespace):
doctest_namespace["np"] = numpy
然後可以直接在 doctest 中使用
# content of numpy.py
def arange():
"""
>>> a = np.arange(10)
>>> len(a)
10
"""
請注意,就像一般的 conftest.py
,固定裝置會在 conftest 所在的目錄樹中被偵測到。這表示如果你將 doctest 放在原始碼中,相關的 conftest.py 需要在同一個目錄樹中。固定裝置不會在同層目錄樹中被偵測到!
略過測試¶
基於與略過一般測試相同的原因,也可以略過 doctest 中的測試。
若要略過 doctest 中的單一檢查,可以使用標準的 doctest.SKIP
指令
def test_random(y):
"""
>>> random.random() # doctest: +SKIP
0.156231223
>>> 1 + 1
2
"""
這將略過第一個檢查,但不會略過第二個。
pytest 也允許在 doctest 中使用標準的 pytest 函式 pytest.skip()
和 pytest.xfail()
,這可能很有用,因為你可以根據外部條件略過/失敗測試
>>> import sys, pytest
>>> if sys.platform.startswith('win'):
... pytest.skip('this doctest does not work on Windows')
...
>>> import fcntl
>>> ...
不過,不建議使用這些函式,因為它們會降低文件字串的可讀性。
注意
pytest.skip()
和 pytest.xfail()
的行為取決於 doctest 是在 Python 檔案(文件字串中)還是包含 doctest 和文字的文字檔案中
Python 模組(文件字串):這些函式只會在那個特定的文件字串中作用,讓同一個模組中的其他文件字串正常執行。
文字檔案:這些函式會略過/失敗檔案中其餘部分的檢查。
替代方案¶
雖然內建的 pytest 支援提供了良好的 doctest 使用功能,但如果你廣泛使用它們,你可能會對那些新增更多功能並包含 pytest 整合的外部套件感興趣
pytest-doctestplus:提供進階的 doctest 支援,並啟用 reStructuredText (“.rst”) 檔案的測試。
Sybil:提供一種方法來測試文件中的範例,方法是從文件來源中分析範例,並在正常測試執行期間評估已分析的範例。