9、pytest -- 集成文檔測試

1. 集成doctest模塊

doctestpython內置的一個標準庫,它可以查找代碼中類似交互式會話形式的註釋,並檢查它們是否正確;

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標準庫自帶的選項

pythondoctest標準庫自帶一些選項,用於定義文檔測試的模式,我們同樣可以在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

GitHub倉庫地址:https://github.com/luizyao/pytest-chinese-doc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章