1. 集成doctest
模塊
doctest是python
內置的一個標准庫,它可以查找代碼中類似交互式會話形式的注釋,並檢查它們是否正確;
1.1. 通過指定文本文件的方式
默認情況下,pytest
會自動收集所有名稱匹配test*.txt
規則的文件,並調用doctest
執行它們;
下面,我們來看一個簡單的例子:
# src/chapter-9/test_example.txt
>>> x = 3
>>> x
3
直接使用pytest
命令就可以執行它:
λ pipenv run pytest src/chapter-9/test_example.txt
====================== test session starts =======================
platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
rootdir: D:\Personal Files\Projects\pytest-chinese-doc
collected 1 item
src\chapter-9\test_example.txt . [100%]
======================= 1 passed in 0.03s ========================
我們也可以使用命令行選項--doctest-glob
添加文件名稱的匹配規則;
例如,匹配rst
格式的文件:
pytest --doctest-glob='*.rst'
注意:
--doctest-glob
可以多次指定,它們之間是或者的關系,並且依舊支持默認的test*.txt
規則;
1.1.1. 文本文件的編碼
doctest
文件的默認編碼是UTF-8,你可以在pytest.ini
中使用doctest_encoding
選項指定新的編碼;例如,使用latin1
編碼:
[pytest]
doctest_encoding = latin1
1.2. 通過編寫文檔字符串的方式
除了文本文件外,pytest
還支持檢查文檔字符串中的注釋;例如:
# src/chapter-9/test_module.py
def something():
'''文檔字符串示例
>>> something()
42
'''
return 42
def test_module():
assert something() == 42
執行時,需要添加--doctest-modules
命令行選項:
λ pipenv run pytest --doctest-modules src/chapter-9/test_module.py
====================== test session starts =======================
platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
rootdir: D:\Personal Files\Projects\pytest-chinese-doc
collected 2 items
src\chapter-9\test_module.py .. [100%]
======================= 2 passed in 0.03s ========================
--doctest-modules
會查找所有名稱匹配*.py
的文件,收集文檔字符串中類似交互式會話形式的注釋,並把每一個文檔字符串作為一個用例來執行,所以上面我們執行了兩個測試,其中一個是文檔測試;
如果想讓pytest --doctest-modules
正確收集到相關注釋,需滿足以下條件:
- 文件名稱符合
*.py
規則,但無需滿足test_*.py
或者*_test.py
規則; - 文檔字符串中的注釋必須是類似
python
交互式會話形式的注釋;
如果你不想每次執行都指定--doctest-modules
選項,可以考慮在pytest.ini
中添加如下配置:
[pytest]
addopts = --doctest-modules
文檔字符串是一個多行字符串,使用
'''
或者"""
包裹;一般推薦形式為,第一行簡述功能,第二行空行,第三行具體描述;並且,可以通過
__doc__
屬性訪問它;例如,上面例子的__doc__
屬性為:>>> print(something.__doc__) 文檔字符串示例 >>> something() 42
1.3. 指定額外的選項
1.3.1. doctest
標准庫自帶的選項
python
的doctest
標准庫自帶一些選項,用於定義文檔測試的模式,我們同樣可以在pytest.ini
中使用這些功能;例如,忽略尾隨的空格:
# src/chapter-9/pytest.ini
doctest_optionflags = NORMALIZE_WHITESPACE
另外,你也可以在行注釋中使能這些選項;
例如,使用# doctest: +NORMALIZE_WHITESPACE
同樣能忽略尾隨的空格:
def something():
'''文檔字符串示例
>>> something() # doctest: +NORMALIZE_WHITESPACE
42
'''
return 42
更多細節可以參考:https://docs.python.org/3/library/doctest.html#option-flags
1.3.2. pytest
自有的選項
ALLOW_BYTES
:在輸出時,剔除字符串的b
前綴;例如,以下文檔測試是成功的:
# src/chapter-9/options.py
def str_bytes():
'''返回字節編碼
>>> str_bytes() # doctest: +ALLOW_BYTES
'bytes'
'''
return b'bytes'
ALLOW_UNICODE
:相似的,在輸出時,剔除字符串的u
前綴;
NUMBER
:為了避免出現以下導致測試失敗的情況:
Expected:
3.14
Got:
3.141592653589793
我們可以通過配置NUMBER
選項,只比較列出的精度:
# src/chapter-9/options.py
def number():
'''浮點數的精度
>>> import math
>>> math.pi # doctest: +NUMBER
3.14
'''
return 1
注意:我們並不建議在全局使能
NUMBER
選項,因為它會修改輸出中所有的浮點數的精度,甚至是在字符串或者列表中;例如,以下文檔測試也是成功的:
# src/chapter-9/options.py def str_number(): '''浮點數字符串的精度 >>> str_number() # doctest: +NUMBER '3.14' ''' return '3.1415'
2. 失敗時繼續執行
默認情況下,對於一個給定的文檔測試,pytest
在遇到第一個失敗點時退出執行;但是,我們可以通過--doctest-continue-on-failure
命令行選項,讓其繼續執行;
例如,對於以下文檔字符串,包含兩個測試點,pytest --doctest-continue-on-failure
會報告兩個錯誤(默認只會報告第一個錯誤):
def str_bytes():
'''返回字節編碼
>>> str_bytes()
'bytes'
>>> import math
>>> math.pi
3.14
'''
return b'bytes'
3. 指定輸出的格式
文檔測試失敗時,你可以通過以下方式更改測試輸出的格式:
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
更多細節可以參考:https://docs.python.org/3/library/doctest.html#doctest.REPORT_UDIFF
4. 文檔測試中使用fixture
通過getfixture
可以讓你在文檔字符串中使用fixture
:
>>> tmp = getfixture('tmpdir')
>>> ...
>>>
5. 文檔測試的命名空間
doctest_namespace fixture
可以用於向運行doctest
測試的命名空間中注入一些信息,它是一個標准的字典對象;
例如,我們在conftest.py
中定義一個方法,注入到doctest
的命名空間中:
# src/chapter-9/conftest.py
import pytest
def func():
return 42
@pytest.fixture(autouse=True)
def add_func(doctest_namespace):
doctest_namespace['function'] = func
可以在文檔字符串中直接使用它:
# src/chapter-9/func.txt
>>> function()
42
6. 跳過文檔測試
pytest 4.4
版本新增功能
我們可以通過pytest.skip
跳過文檔測試;例如,跳過Windows
系統上的文檔測試:
>>> import sys, pytest
>>> if sys.platform.startswith('win'):
... pytest.skip('this doctest does not work on Windows')
>>> function()
42