Historische Notizen

Diese Seite listet Funktionen oder Verhaltensweisen früherer Versionen von pytest auf, die sich im Laufe der Jahre geändert haben. Sie sind hier als historische Notiz aufgeführt, damit Benutzer, die alten Code betrachten, relevante Dokumentationen finden können.

Marker-Überarbeitung und Iteration

Geändert in Version 3.6.

Die Marker-Implementierung von pytest funktionierte traditionell, indem einfach das Attribut __dict__ von Funktionen aktualisiert wurde, um Marker kumulativ hinzuzufügen. Infolgedessen wurden Marker unbeabsichtigt auf überraschende Weise durch Klassen-Hierarchien weitergegeben. Darüber hinaus war die API zum Abrufen von Markern inkonsistent, da Marker aus der Parametrisierung anders gespeichert wurden als Marker, die mit dem Dekorator @pytest.mark angewendet wurden, und Marker, die über node.add_marker hinzugefügt wurden.

Dieser Zustand machte es technisch fast unmöglich, Daten aus Markern korrekt zu verwenden, ohne ein tiefes Verständnis der Interna zu haben, was zu subtilen und schwer verständlichen Fehlern bei fortgeschritteneren Anwendungen führte.

Je nachdem, wie ein Marker deklariert/geändert wurde, erhielt man entweder ein MarkerInfo, das Marker von Geschwisterklassen enthalten konnte, MarkDecorators, wenn Marks aus der Parametrisierung stammten oder vom Dekorator @pytest.mark stammten, oder eine Aktion über node.add_marker, die frühere Marks verwarf. Außerdem verhält sich MarkerInfo wie ein einzelner Mark, obwohl es eigentlich eine zusammengeführte Ansicht mehrerer Marks mit demselben Namen darstellt.

Darüber hinaus waren Marker für Module, Klassen und Funktionen/Methoden nicht auf die gleiche Weise zugänglich. Tatsächlich waren Marker nur in Funktionen zugänglich, auch wenn sie auf Klassen/Modulen deklariert waren.

Eine neue API zum Zugriff auf Marker wurde in pytest 3.6 eingeführt, um die Probleme mit dem ursprünglichen Design zu lösen, indem die Methode _pytest.nodes.Node.iter_markers() bereitgestellt wurde, um Marker auf konsistente Weise zu iterieren und die Interna zu überarbeiten, was einen Großteil der Probleme des ursprünglichen Designs behoben hat.

Aktualisierung von Code

Die alte Funktion Node.get_marker(name) gilt als veraltet, da sie ein internes MarkerInfo-Objekt zurückgibt, das den zusammengeführten Namen, *args und **kwargs aller Marker enthält, die auf diesen Knoten angewendet werden.

Im Allgemeinen gibt es zwei Szenarien, wie Marker gehandhabt werden sollten

1. Marks überschreiben sich gegenseitig. Die Reihenfolge ist wichtig, aber Sie möchten Ihren Mark nur als einzelnes Element betrachten. Z. B. kann log_level('info') auf Modulebene durch log_level('debug') für einen bestimmten Test überschrieben werden.

In diesem Fall verwenden Sie Node.get_closest_marker(name)

# replace this:
marker = item.get_marker("log_level")
if marker:
    level = marker.args[0]

# by this:
marker = item.get_closest_marker("log_level")
if marker:
    level = marker.args[0]

2. Marks komponieren sich additiv. Z. B. bedeuten skipif(condition) Marks, dass Sie alle auswerten möchten, die Reihenfolge spielt nicht einmal eine Rolle. Sie möchten Ihre Marks hier wahrscheinlich als Menge betrachten.

In diesem Fall iterieren Sie über jeden Mark und behandeln Sie dessen *args und **kwargs einzeln.

# replace this
skipif = item.get_marker("skipif")
if skipif:
    for condition in skipif.args:
        # eval condition
        ...

# by this:
for skipif in item.iter_markers("skipif"):
    condition = skipif.args[0]
    # eval condition

Wenn Sie sich unsicher sind oder Fragen haben, öffnen Sie bitte ein Issue.

Cache-Plugin in den Kern integriert

Die Funktionalität des Kern-Cache-Plugins wurde zuvor als Drittanbieter-Plugin namens pytest-cache vertrieben. Das Kern-Plugin ist kompatibel in Bezug auf Kommandozeilenoptionen und API-Nutzung, mit der Ausnahme, dass Sie nur JSON-serialisierbare Daten zwischen Testläufen speichern/empfangen können.

Funcargs und pytest_funcarg__

In Versionen vor 2.3 gab es keinen @pytest.fixture-Marker, und Sie mussten ein magisches Präfix pytest_funcarg__NAME für die Fixture-Fabrik verwenden. Dies bleibt und wird weiterhin unterstützt, wird aber nicht mehr als primäres Mittel zur Deklaration von Fixture-Funktionen beworben.

Der Dekorator @pytest.yield_fixture

Vor Version 2.10 musste man, um eine yield-Anweisung zur Ausführung von Aufräumarbeiten zu verwenden, eine Fixture mit dem yield_fixture-Marker markieren. Ab Version 2.10 können normale Fixtures yield direkt verwenden, sodass der Dekorator yield_fixture nicht mehr benötigt wird und als veraltet gilt.

Der Header [pytest] in setup.cfg

Vor Version 3.0 war der unterstützte Abschnittsname [pytest]. Aufgrund von Kollisionen mit einigen distutils-Befehlen ist der empfohlene Abschnittsname für setup.cfg-Dateien jetzt [tool:pytest].

Beachten Sie, dass für pytest.ini und tox.ini-Dateien der Abschnittsname [pytest] lautet.

Anwenden von Marks auf Parameter von @pytest.mark.parametrize

Vor Version 3.1 wurde der unterstützte Mechanismus zur Markierung von Werten mit der Syntax verwendet

import pytest


@pytest.mark.parametrize(
    "test_input,expected", [("3+5", 8), ("2+4", 6), pytest.mark.xfail(("6*9", 42))]
)
def test_eval(test_input, expected):
    assert eval(test_input) == expected

Dies war ein anfänglicher Hack zur Unterstützung der Funktion, erwies sich jedoch bald als unvollständig und fehlerhaft für die Übergabe von Funktionen oder die Anwendung mehrerer Marks mit demselben Namen, aber unterschiedlichen Parametern.

Die alte Syntax soll in pytest-4.0 entfernt werden.

Argumentnamen von @pytest.mark.parametrize als Tupel

In Versionen vor 2.4 musste man die Argumentnamen als Tupel angeben. Dies bleibt gültig, aber die einfachere durch Kommas getrennte Zeichenkette "name1,name2,..." wird jetzt zuerst beworben, da sie einfacher zu schreiben ist und weniger Zeilenrauschen erzeugt.

setup: ist jetzt eine „autouse fixture“

Während der Entwicklung vor der Veröffentlichung von pytest-2.3 wurde der Name pytest.setup verwendet, aber vor der Veröffentlichung wurde er umbenannt und in den allgemeinen Fixture-Mechanismus verschoben, nämlich in Autouse-Fixtures (Fixtures, die Sie nicht anfordern müssen)

Bedingungen als Zeichenketten statt als Booleans

Vor pytest-2.4 war die einzige Möglichkeit, skipif/xfail-Bedingungen anzugeben, die Verwendung von Zeichenketten

import sys


@pytest.mark.skipif("sys.version_info >= (3,3)")
def test_function(): ...

Während des Setups der Testfunktion wird die skipif-Bedingung durch Aufruf von eval('sys.version_info >= (3,0)', namespace) ausgewertet. Der Namespace enthält alle globalen Variablen des Moduls und als Minimum os und sys.

Seit pytest-2.4 werden boolesche Bedingungen bevorzugt, da Marker dann frei zwischen Testmodulen importiert werden können. Bei Zeichenketten müssen Sie nicht nur den Marker, sondern auch alle vom Marker verwendeten Variablen importieren, was die Kapselung verletzt.

Der Grund für die Angabe der Bedingung als Zeichenkette war, dass pytest eine Zusammenfassung der Skip-Bedingungen ausschließlich auf Basis der Bedingungszeichenkette berichten kann. Mit booleschen Bedingungen müssen Sie eine reason-Zeichenkette angeben.

Beachten Sie, dass Zeichenkettenbedingungen weiterhin vollständig unterstützt werden und Sie diese frei verwenden können, wenn Sie keine Notwendigkeit für Cross-Import von Markern haben.

Die Auswertung einer Bedingungszeichenkette in pytest.mark.skipif(conditionstring) oder pytest.mark.xfail(conditionstring) erfolgt in einem Namespace-Dictionary, das wie folgt aufgebaut ist

  • Der Namespace wird initialisiert, indem die Module sys und os sowie das pytest config-Objekt darin platziert werden.

  • aktualisiert mit den globalen Variablen des Testmoduls, für das der Ausdruck angewendet wird.

Das pytest config-Objekt ermöglicht es Ihnen, basierend auf einem Testkonfigurationswert zu überspringen, den Sie möglicherweise hinzugefügt haben

@pytest.mark.skipif("not config.getvalue('db')")
def test_function(): ...

Das Äquivalent mit „booleschen Bedingungen“ ist

@pytest.mark.skipif(not pytest.config.getvalue("db"), reason="--db was not specified")
def test_function():
    pass

Hinweis

Sie können pytest.config.getvalue() nicht in Code verwenden, der vor der Argumentanalyse von pytest importiert wird. Zum Beispiel werden conftest.py-Dateien vor der Kommandozeilenanalyse importiert, und daher wird config.getvalue() nicht korrekt ausgeführt.

pytest.set_trace()

Vor Version 2.4 musste man, um einen Haltepunkt im Code zu setzen, pytest.set_trace() verwenden

import pytest


def test_function():
    ...
    pytest.set_trace()  # invoke PDB debugger and tracing

Dies ist nicht mehr notwendig und man kann den nativen Aufruf import pdb;pdb.set_trace() direkt verwenden.

Weitere Details finden Sie unter Haltepunkte setzen.

„Compat“-Eigenschaften

Der Zugriff auf Module, Function, Class, Instance, File und Item über Node-Instanzen wurde lange als veraltet dokumentiert, begann aber ab pytest 3.9 und aufwärts Warnungen auszugeben.

Benutzer sollten einfach import pytest und auf diese Objekte über das pytest-Modul zugreifen.