Doctests ausführen

Standardmäßig werden alle Dateien, die dem Muster test*.txt entsprechen, über das Standardmodul doctest von Python ausgeführt. Sie können das Muster ändern, indem Sie

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

auf der Kommandozeile angeben. --doctest-glob kann mehrmals in der Kommandozeile angegeben werden.

Wenn Sie dann eine Textdatei wie diese haben

# content of test_example.txt

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

dann können Sie pytest einfach direkt aufrufen

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item

test_example.txt .                                                   [100%]

============================ 1 passed in 0.12s =============================

Standardmäßig sammelt pytest test*.txt Dateien auf der Suche nach Doctest-Direktiven, aber Sie können zusätzliche Globs über die Option --doctest-glob (mehrfach erlaubt) übergeben.

Zusätzlich zu Textdateien können Sie Doctests auch direkt aus Docstrings Ihrer Klassen und Funktionen ausführen, einschließlich aus Testmodulen.

# 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-9.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 =============================

Sie können diese Änderungen in Ihrem Projekt dauerhaft machen, indem Sie sie in eine Konfigurationsdatei wie diese eintragen

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

Encoding

Das Standard-Encoding ist UTF-8, aber Sie können das Encoding, das für diese Doctest-Dateien verwendet wird, mit der Konfigurationsoption doctest_encoding festlegen.

[pytest]
doctest_encoding = "latin1"
[pytest]
doctest_encoding = latin1

Verwendung von 'doctest'-Optionen

Das Standardmodul doctest von Python bietet einige Optionen zur Konfiguration der Strenge von Doctests. In pytest können Sie diese Flags über die Konfigurationsdatei aktivieren.

Um beispielsweise nachgestellte Leerzeichen und lange Exception-Stacktraces zu ignorieren, können Sie einfach Folgendes schreiben

[pytest]
doctest_optionflags = ["NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"]
[pytest]
doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL

Alternativ können Optionen durch einen Inline-Kommentar im Doctest selbst aktiviert werden.

>>> something_that_raises()  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: ...

pytest führt auch neue Optionen ein

  • ALLOW_UNICODE: Wenn aktiviert, wird das Präfix u von Unicode-Strings in erwarteten Doctest-Ausgaben entfernt. Dies ermöglicht die unveränderte Ausführung von Doctests in Python 2 und Python 3.

  • ALLOW_BYTES: Ähnlich wird das Präfix b von Byte-Strings in erwarteten Doctest-Ausgaben entfernt.

  • NUMBER: Wenn aktiviert, müssen Gleitkommazahlen nur bis zur von Ihnen im erwarteten Doctest-Output angegebenen Genauigkeit übereinstimmen. Die Zahlen werden mit pytest.approx() mit einer relativen Toleranz verglichen, die der Genauigkeit entspricht. Zum Beispiel müsste die folgende Ausgabe nur bis zu 2 Dezimalstellen übereinstimmen, wenn 3.14 mit pytest.approx(math.pi, rel=10**-2) verglichen wird.

    >>> math.pi
    3.14
    

    Wenn Sie 3.1416 geschrieben hätten, müsste die tatsächliche Ausgabe ungefähr auf 4 Dezimalstellen übereinstimmen; und so weiter.

    Dies vermeidet falsch-positive Ergebnisse aufgrund begrenzter Gleitkommapräzision, wie hier:

    Expected:
        0.233
    Got:
        0.23300000000000001
    

    NUMBER unterstützt auch Listen von Gleitkommazahlen – tatsächlich gleicht es Gleitkommazahlen ab, die irgendwo in der Ausgabe vorkommen, selbst innerhalb eines Strings! Das bedeutet, dass es möglicherweise nicht ratsam ist, dies global in doctest_optionflags in Ihrer Konfigurationsdatei zu aktivieren.

    Hinzugefügt in Version 5.1.

Fortfahren bei Fehlern

Standardmäßig würde pytest nur den ersten Fehler für einen gegebenen Doctest melden. Wenn Sie den Test auch bei Fehlern fortsetzen möchten, tun Sie Folgendes:

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

Ausgabeformat

Sie können das Diff-Ausgabeformat bei Fehlern für Ihre Doctests ändern, indem Sie eine der Standardformatoptionen des Doctest-Moduls verwenden (siehe doctest.REPORT_UDIFF, doctest.REPORT_CDIFF, doctest.REPORT_NDIFF, doctest.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-spezifische Funktionen

Einige Funktionen werden bereitgestellt, um das Schreiben von Doctests zu erleichtern oder eine bessere Integration mit Ihrer bestehenden Testsuite zu ermöglichen. Beachten Sie jedoch, dass Sie durch die Verwendung dieser Funktionen Ihre Doctests mit dem Standardmodul doctests inkompatibel machen.

Verwendung von Fixtures

Es ist möglich, Fixtures mit dem getfixture-Helfer zu verwenden.

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

Beachten Sie, dass das Fixture an einem Ort definiert sein muss, der für pytest sichtbar ist, z. B. in einer conftest.py-Datei oder einem Plugin; normale Python-Dateien, die Docstrings enthalten, werden normalerweise nicht nach Fixtures durchsucht, es sei denn, sie werden explizit über python_files konfiguriert.

Außerdem werden die Markierung usefixtures und als autouse markierte Fixtures beim Ausführen von Text-Doctest-Dateien unterstützt.

'doctest_namespace'-Fixture

Das doctest_namespace-Fixture kann verwendet werden, um Elemente in den Namespace einzufügen, in dem Ihre Doctests ausgeführt werden. Es ist dazu gedacht, innerhalb Ihrer eigenen Fixtures verwendet zu werden, um die Tests, die sie verwenden, mit Kontext zu versorgen.

doctest_namespace ist ein Standard-Objekt vom Typ dict, in das Sie die Objekte einfügen, die im Doctest-Namespace erscheinen sollen.

# content of conftest.py
import pytest
import numpy


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

was dann direkt in Ihren Doctests verwendet werden kann

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

Beachten Sie, dass die Fixtures, wie die normale conftest.py, im Verzeichnisbaum gefunden werden, in dem sich conftest befindet. Das bedeutet, wenn Sie Ihren Doctest mit Ihrem Quellcode zusammenlegen, muss die entsprechende conftest.py im selben Verzeichnisbaum liegen. Fixtures werden nicht in einem Geschwister-Verzeichnisbaum gefunden!

Tests überspringen

Aus den gleichen Gründen, aus denen man normale Tests überspringen möchte, ist es auch möglich, Tests innerhalb von Doctests zu überspringen.

Um eine einzelne Prüfung innerhalb eines Doctests zu überspringen, können Sie die Standarddirektive doctest.SKIP verwenden.

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

    >>> 1 + 1
    2
    """

Dies überspringt die erste Prüfung, aber nicht die zweite.

pytest erlaubt auch die Verwendung der Standard-pytest-Funktionen pytest.skip() und pytest.xfail() innerhalb von Doctests, was nützlich sein kann, da Sie dann Tests basierend auf externen Bedingungen überspringen/xfailen können.

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

Die Verwendung dieser Funktionen wird jedoch nicht empfohlen, da sie die Lesbarkeit des Docstrings verringert.

Hinweis

pytest.skip() und pytest.xfail() verhalten sich unterschiedlich, je nachdem, ob die Doctests in einer Python-Datei (in Docstrings) oder in einer Textdatei mit Doctests, die mit Text durchsetzt sind, enthalten sind.

  • Python-Module (Docstrings): Die Funktionen wirken nur in diesem speziellen Docstring, während andere Docstrings im selben Modul normal ausgeführt werden.

  • Textdateien: Die Funktionen überspringen/xfailen die Prüfungen für den Rest der gesamten Datei.

Alternativen

Während die integrierte pytest-Unterstützung eine gute Funktionalität für die Verwendung von Doctests bietet, könnten Sie, wenn Sie sie ausgiebig nutzen, an folgenden externen Paketen interessiert sein, die viele weitere Funktionen hinzufügen und pytest-Integration beinhalten:

  • pytest-doctestplus: Bietet erweiterte Doctest-Unterstützung und ermöglicht das Testen von reStructuredText (".rst") Dateien.

  • Sybil: Bietet eine Möglichkeit, Beispiele in Ihrer Dokumentation zu testen, indem sie aus dem Quelldokumentation geparst und die geparsten Beispiele als Teil Ihres normalen Testlaufs ausgewertet werden.