小门板儿

Menu

pytest:模块和测试文件的Doctest集成

一、前言

doctest从字面意思上看,那就是文档测试。doctest是python里面自带的一个模块,它实际上是单元测试的一种。 官方解释:doctest 模块会搜索那些看起来像交互式会话的 Python 代码片段,然后尝试执行并验证结果

默认情况下,所有符合 test*.txt 模式的测试文件会被python标准的 doctest 模块执行,你可以通过命令行参数来改变这种默认行为:

pytest --doctest-glob="*.rst"

–doctest-glob 可以在命令行中被多次指定

如下,一个.txt的文件

hello ,this is a doctest
>>> x=3 
>>> x 
3

执行测试:

D:\pytest\learn01\a>pytest -q test_content.txt
 .[100%]
1 passed in 0.03s

在txt文件中,>>> x 表示输出x,下面的3表示期望值是3,如果x是3,则测试通过,不是3,则测试失败

默认情况下,pytest会收集test*.txt文件寻找doctest指令,你也可以使用 --doctest-glob 指令来传递一个额外的设置。 除了文本文件,你还可以直接从类,函数,模块的docstring中执行doctest:

doctest测试用例可以放在两个地方

def something():
    """a doctest in a docstring
    >>> something()
    42
    """
    return 42

执行测试:

D:\pytest\learn01\a>pytest -v --doctest-modules mymoudle.py
=========================== test session starts ===========================
platform win32 -- Python 3.7.3, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- d:\python3.7\python\python3.exe
cachedir: .pytest_cache
rootdir: D:\pytest\learn01, configfile: pytest.ini
collected 1 item  
mymoudle.py::a.mymoudle.something PASSED       [100%]
================= 1 passed in 0.03s =========

可以在你的项目中固化这些设置,方法是写在 pytest.ini 中:

# content of pytest.ini
[pytest]
addopts = --doctest-modules

二、编码

默认编码是 UTF-8 ,但可以使用 doctest_encoding ini选项:

# content of pytest.ini
[pytest]
doctest_encoding = latin1

三、使用“doctest”选项

Python 的标准 doctest 模块提供了一些选项来配置文档测试的严格程度,在pytest中,可以使用配置文件启用这些标志。

例如,要使pytest忽略尾随空格并忽略冗长的异常堆栈跟踪,只需如下配置

[pytest]
doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL

pytest还引入了新的选项:

ALLOW_UNICODE :启用后, u 在预期的doctest输出中,前缀从unicode字符串中剥离。这使得doctest可以在python2和python3中运行。

ALLOW_BYTES :类似地 b 前缀将从预期doctest输出中的字节字符串中剥离。

# src/chapter-9/options.py

def str_bytes():
  '''返回字节编码

  >>> str_bytes() # doctest: +ALLOW_BYTES
  'bytes'
  '''
  return b'bytes'

NUMBER :启用时,浮点数只需与在预期doctest输出中写入的精度匹配

>>> import math
>>> math.pi
3.14
执行测试
D:\pytest\learn01\a>pytest -q test_content.txt
F                [100%]
============== FAILURES ========
_______________ [doctest] test_content.txt _______________
001 >>> import math
002 >>> math.pi
Expected:
    3.14
Got:
    3.141592653589793
​
D:\pytest\learn01\a\test_content.txt:2: DocTestFailure
============================================== short test summary info ===========

带上number选项

def number():
  '''浮点数的精度

  >>> import math
  >>> math.pi # doctest: +NUMBER
  3.14
  '''
  return 1

def test_number():
  assert number()==1

四、doctest独立文件

这个例子展示如何将doctest用例放到一个独立的文件中。
'>>>' 开头的行就是doctest测试用例。
不带 '>>>' 的行就是测试用例的输出。
如果实际运行的结果与期望的结果不一致,就标记为测试失败。 

>>> from unnecessary_math import multiply
>>> multiply(3, 4)
12
>>> multiply('a', 3)
'aaa'

五、失败时继续执行

默认情况下,对于一个给定的文档测试,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'

六、输出格式

在选项中使用标准doctest模块格式之一,可以在doctest失败时更改doctest的diff输出格式(请参见 doctest.REPORT_UDIFFdoctest.REPORT_CDIFFdoctest.REPORT_NDIFFdoctest.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的特殊功能

1、使用夹具

在测试文档中使用自带的tmpdir夹具

>>> tmp = getfixture('tmpdir')
>>> print(tmp)
ass

测试结果

001 >>> tmp = getfixture('tmpdir')
002 >>> print(tmp)
Expected:
    ass
Got:
    C:\Users\admin\AppData\Local\Temp\pytest-of-admin\pytest-28\test_content_txt0

注意,夹具必须定义在可以被pytest发现的地方,如 conftest.py 或者插件中;普通含有文档测试的py文件不会自动扫描夹具,除非在配置文件的 python_files 中专门配置过。同时,在执行文档测试的时候,使用usefixtures定义的和标记了autouse自动使用的夹具会被支持。

2、doctest_namespace夹具

doctest_namespace夹具可以用来向运行的doctests命名空间植入项目。这被用来在自己的夹具中为将要使用这个夹具的文档测试提供命名空间上下文的支持。doctest_namespace是一个标准的字典对象,里面放着你期望在文档测试中出现的命名空间

#content of contest.py
import math
import pytest

@pytest.fixture(autouse=True)
def add_np(doctest_namespace):
    doctest_namespace["ma"] = math

#content of mymoudle.py
def get_pi():
    '''
    >>> ma.pi #doctest: +NUMBER
    3.14
    '''
    pass

3、动态的跳过测试

可以使用 pytest.skip 动态的跳过文档测试

要跳过doctest中的单个检查,可以使用标准 doctest.SKIP 指令:

def test_random(y):
    """
    >>> random.random()  # doctest: +SKIP
    0.156231223

    >>> 1 + 1
    2
    """

这将跳过第一次检查,但不会跳过第二次

pytest还允许使用标准pytest函数 pytest.skip()pytest.xfail() 在doctest内部,这可能很有用,因为您可以根据外部条件跳过/xFAIL测试:

>>> import sys, pytest
>>> if sys.platform.startswith('win'):
...     pytest.skip('this doctest does not work on Windows')

但是,不鼓励使用这些函数,因为它降低了文档字符串的可读性

注解:pytest.skip()pytest.xfail() 根据doctest是在Python文件(在docstring中)还是在包含混合了文本的doctest的文本文件中,行为会有所不同:

参考文献:https://www.osgeo.cn/pytest/doctest.html#using-doctest-options

— 于 共写了4377个字
— 标签:

评论已关闭。