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 は doctest ディレクティブを探して test*.txt ファイルを収集しますが、--doctest-glob オプションを使用して追加のグロブを渡すことができます (複数指定可能) 。

テキストファイルに加えて、テストモジュールを含むクラスや関数の docstring から直接 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 テストの厳密さを構成するためのいくつかの options を提供します。 pytest では、構成ファイルを使用してこれらのフラグを有効にできます。

たとえば、pytest が末尾の空白を無視し、長い例外スタックトレースを無視するようにするには、次のように記述します:

[pytest]
doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL

または、オプションは doctest 自体のインラインコメントによって有効にすることもできます:

>>> 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.14pytest.approx(math.pi, rel=10**-2) と比較するときに小数点以下 2 桁まで一致する必要があります:

    >>> math.pi
    3.14
    

    3.1416 と記述した場合、実際の出力は約小数点以下 4 桁まで一致する必要があります。 などなど。

    これにより、次のような浮動小数点精度の制限によって引き起こされる偽陽性を回避できます:

    Expected:
        0.233
    Got:
        0.23300000000000001
    

    NUMBER は浮動小数点数のリストもサポートしています。 実際、出力内のどこにでも表示される浮動小数点数と一致します。 文字列内でも同様です!これは、構成ファイルの doctest_optionflags でグローバルに有効にするのが適切でない場合があることを意味します。

    Added in version 5.1.

失敗時に続行

デフォルトでは、pytest は指定された doctest の最初の失敗のみを報告します。 失敗があってもテストを続行したい場合は、次のようにします:

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

出力形式

オプションで標準の 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 固有の機能

いくつかの機能が提供されており、doctest の記述を容易にしたり、既存のテストスイートとの統合を向上させたりします。 ただし、これらの機能を使用すると、doctest が標準の doctests モジュールと互換性がなくなることに注意してください。

フィクスチャの使用

getfixture ヘルパーを使用してフィクスチャを使用することができます:

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

フィクスチャは pytest から見える場所に定義する必要があることに注意してください。 たとえば、conftest.py ファイルやプラグインです。 docstring を含む通常の Python ファイルは、python_files で明示的に構成されていない限り、通常はフィクスチャのスキャン対象にはなりません。

また、テキスト doctest ファイルを実行する際には、usefixtures マークおよび autouse としてマークされたフィクスチャがサポートされます。

'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
    """

これにより、最初のチェックはスキップされますが、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')
...
>>> import fcntl
>>> ...

ただし、これらの関数を使用することは、docstring の可読性を低下させるため推奨されません。

注釈

pytest.skip() および pytest.xfail() は、doctest が Python ファイル (docstring 内) にあるか、テキストと混在する doctest を含むテキストファイルにあるかによって動作が異なります:

  • Python モジュール (docstring) : 関数はその特定の docstring 内でのみ動作し、同じモジュール内の他の docstring を通常どおり実行させます。

  • テキストファイル: 関数は、ファイル全体の残りのチェックをスキップ/xfail します。

代替案

組み込みの pytest サポートは、doctest を使用するための優れた機能セットを提供しますが、doctest を広範囲に使用する場合は、さらに多くの機能を追加し、pytest 統合を含むこれらの外部パッケージに興味があるかもしれません:

  • pytest-doctestplus: 高度な doctest サポートを提供し、reStructuredText (".rst") ファイルのテストを可能にします。

  • Sybil: ドキュメントソースから解析し、解析された例を通常のテスト実行の一部として評価することで、ドキュメント内の例をテストする方法を提供します。