阅读(4440) (0)

pytest 核心功能-运行doctests

2022-03-17 17:33:39 更新

默认情况下,所有匹配​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-7.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。

除了文本文件,你也可以直接从你的类和函数的文档字符串执行​doctests​,包括从测试模块:

# 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-7.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输出中,​u​前缀将从unicode字符串中剥离。这使得文档测试可以在Python 2和Python 3中运行。
  • ALLOW_BYTES​:类似地,​b​前缀从预期doctest输出的字节字符串中剥离。
  • NUMBER​:当启用时,浮点数只需要匹配预期​doctest​输出中所写的精度。例如,下面的输出只需要匹配小数点后两位:
>>> math.pi
3.14

如果您写的是3.1416,那么实际的输出将需要匹配小数点后4位。

这避免了由有限的浮点精度引起的误报,如下所示:

Expected:
    0.233
Got:
    0.23300000000000001

NUMBER​还支持浮点数列表——事实上,它匹配出现在输出中的任何位置的浮点数,甚至在字符串中!这意味着在配置文件中全局启用​doctest_optionflags​可能是不合适的。

继续失败

默认情况下,pytest只会报告给定​doctest​的第一次失败。如果你在测试失败后仍想继续测试,请:

pytest --doctest-modules --doctest-continue-on-failure

输出格式

通过在选项中使用标准​doctest​模块格式之一,可以更改​doctest​失败时的diff输出格式

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-specific特性

提供了一些特性,使编写文档测试更容易,或者与现有的测试套件更好地集成。但是请记住,通过使用这些特性,您将使您的​doctests​与标准​doctests​模块不兼容。

使用fixtures

可以使用​getfixture​ helper来使用​fixture​:

# content of example.rst
>>> tmp = getfixture('tmp_path')
>>> ...
>>>

请注意,​fixture​需要定义在pytest可见的地方,例如,​conftest.py​文件或插件;包含文档字符串的普通python文件通常不会扫描​fixture​,除非由​python_files​显式配置。

此外,当执行文本​doctest​文件时,也支持​usefixtures​标记和​fixtures​标记为​autuse​。

‘doctest_namespace’ fixture

doctest_namespace fixture​可以用来向运行​doctest​的命名空间注入项目。它打算在您自己的​fixture​中使用,以提供与上下文一起使用它们的测试。

Doctest_namespace​是一个标准​dict​对象,你可以将你想要出现在​doctest​命名空间中的对象放入其中:

# content of conftest.py
import numpy


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

然后可以直接在您的文档测试中使用:

# content of numpy.py
def arange():
    """
    >>> a = np.arange(10)
    >>> len(a)
    10
    """
    pass

注意,与普通的​conftest.py​一样,​fixture​是在​conftest​所在的目录树中发现的。这意味着,如果您将​doctest​与源代码放在一起,那么相关的​conftest.py​需要位于相同的目录树中。在同级目录树中无法发现​fixture!

跳过测试

出于同样的原因,人们可能希望跳过普通测试,也可以跳过 ​doctests中的测试。

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

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

    >>> 1 + 1
    2
    """

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

Pytest还允许在​doctests​中使用标准的Pytest函数​Pytest .skip()​和​Pytest .xfail()​,这可能很有用,因为你可以根据外部条件跳过​xfail​测试:

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

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

pytest.skip()​和​pytest.xfail()​的行为不同,取决于文档测试是在Python文件(文档字符串)中,还是包含混杂文本的文档测试的文本文件中:

  • Python模块(​docstrings​):这些函数只在特定的文档字符串中起作用,让同一模块中的其他文档字符串正常执行。
  • 文本文件:函数将跳过​xfail​检查整个文件的其余部分。