Konfiguration¶
Mit Konfigurationsdateien könnt ihr den Ablauf von pytest beeinflussen. Wenn ihr
immer wieder bestimmte Optionen in euren Tests verwendet, wie --verbose
oder --strict-markers, könnt ihr diese in einer Konfigurationsdatei
ablegen und müsst sie nicht immer wieder eingeben. Zusätzlich zu den
Konfigurationsdateien gibt es eine Handvoll anderer Dateien, die bei der
Verwendung von pytest nützlich sind, um die Arbeit beim Schreiben und Ausführen
von Tests zu erleichtern:
pyproject.tomlDies ist die wichtigste Konfigurationsdatei von pytest, mit der ihr das Standardverhalten von pytest ändern könnt. Sie legt auch das Stammverzeichnis von pytest fest, oder
rootdir.conftest.pyDiese Datei enthält Test-Fixtures und Hook-Funktionen. Sie kann in
rootdiroder in einem beliebigen Unterverzeichnis existieren.__init__.pyWenn diese Datei in Test-Unterverzeichnissen abgelegt wird, ermöglicht sie die Verwendung identischer Testdateinamen in mehreren Testverzeichnissen.
Wenn ihr bereits eine tox.ini, pytest.ini oder setup.cfg
in eurem Projekt habt, können sie an die Stelle der Konfiguration in
pyproject.toml-Datei treten: tox.ini wird von tox
verwendet, setup.cfg wird für die Paketierung von Python-Projekten
verwendet und kann zum Speichern von Einstellungen für verschiedene Werkzeuge,
einschließlich pytest, verwendet werden.
Ihr solltet eine Konfigurationsdatei haben, entweder in pyproject.toml,
pytest.ini, tox.ini oder setup.cfg.
Die Konfigurationsdatei legt das oberste Verzeichnis fest, von dem aus
pytest gestartet wird.
Schauen wir uns einige dieser Dateien im Zusammenhang mit einer Projekt-Verzeichnisstruktur an:
cusy.tasks
├── …
├── pyproject.toml
├── src
│ └── …
└── tests
├── __init__.py
├── conftest.py
└── test_….py
Im Falle des cusy.tasks-Projekts, das wir bisher zum Testen verwendet haben,
gibt es auf der obersten Ebene eine pyproject.toml-Datei und ein
Verzeichnis tests. Wir werden uns auf diese Struktur beziehen, wenn wir
im weiteren Verlauf dieses Abschnitts über die verschiedenen Dateien sprechen.
Speichern von Einstellungen und Optionen in pyproject.toml¶
Die pyproject.toml-Datei war ursprünglich für die Paketierung von
Python-Projekten gedacht; sie kann jedoch auch für die Definition von
Projekteinstellungen verwendet werden.
[tool.pytest]
minversion = "9.0"
addopts = [
"--strict-markers",
"--strict-config",
"-ra",
]
testpaths = [
"tests",
]
markers = [
"exception: Only run expected exceptions",
"finish: Only run finish tests",
"smoke: Small subset of all tests",
"num_tasks: Number of tasks to be pre-filled for the tasks_db fixture",
]
[tool.pytest] kennzeichnet den Beginn des pytest-Abschnitts. Danach folgen
die einzelnen Einstellungen. Bei Konfigurationseinstellungen, die mehr als einen
Wert zulassen, können die Werte entweder in eine oder in mehrere Zeilen
geschrieben werden in der Form EINSTELLUNG = ["WERT1", "WERT2"].
Dieses Beispiel ist eine einfache pytest-Konfiguration in der
pyproject.toml-Datei, die ich so, oder so ähnlich in fast allen meinen
Projekten verwende. Gehen wir kurz die einzelnen Zeilen durch:
minversionlegt die kleinste pytest-Version fest.
addoptserlaubt die Angabe der pytest-Optionen, die wir immer in diesem Projekt ausführen wollen.
--strict-markersweist pytest an, bei jedem nicht registrierten Marker, der im Testcode auftaucht, einen Fehler statt einer Warnung auszugeben. Hierdurch können wir Tippfehler bei Marker-Namen vermeiden.
--strict-configweist pytest an, wenn beim Parsen von Konfigurationsdateien Schwierigkeiten auftauchen, nicht nur eine Warnung sondern einen Fehler auszugeben. Damit vermeiden wir, dass Tippfehler in der Konfigurationsdatei unbemerkt bleiben.
-raweist pytest an, am Ende eines Testlaufs nicht nur zusätzliche Informationen zu Failures und Errors anzuzeigen sondern auch eine Testzusammenfassung.
-rzeigt zusätzliche Informationen zur Testzusammenfassung an.
azeigt alle außer den bestanden Tests an. Dies fügt den Failures und Errors die Informationen
skipped,xfailedoderxpassedhinzu.
testpaths = ["tests",]teilt pytest mit, wo es nach Tests suchen soll, wenn ihr auf der Kommandozeile keinen Datei- oder Verzeichnisnamen angegeben habt. In unserem Fall sucht pytest im
tests-Verzeichnis.Auf den ersten Blick mag es überflüssig erscheinen,
testpathsauftestszu setzen, da pytest sowieso dort sucht, und wir keinetest_-Dateien in unserensrc- oderdocs-Verzeichnissen haben. Allerdings kann die Angabe einestestpaths-Verzeichnisses ein wenig Startzeit sparen, besonders wenn unseresrc- oderdocs- oder andere Verzeichnisse recht groß sind.markers = ["exception: Only run expected exceptions", …]wird verwendet, um Marker zu deklarieren, wie in Auswahl von Tests mit eigenen Markern beschrieben.
Siehe auch
In den Konfigurationsdateien könnt ihr viele weitere
Konfigurationseinstellungen und Befehlszeilen-Optionen angeben, die ihr euch
mit dem Befehl pytest --help anzeigen lassen könnt.
Andere Konfigurationsdateien verwenden¶
Wenn ihr Tests für ein Projekt schreibt, das bereits eine
pytest.ini, tox.ini oder setup.cfg-Datei hat, könnt
ihr eure pytest-Konfigurationseinstellungen auch in einer dieser alternativen
Konfigurationsdateien speichern. Die Syntax der *.ini-Dateien
unterscheidet sich ein wenig, daher werden wir uns beide Dateien genauer
ansehen.
pytest.ini¶
Die pytest.ini-Datei war ursprünglich für die Konfiguration von pytest
gedacht.
Da TOML
ein anderer Standard für Konfigurationsdateien ist als .ini-Dateien, ist
das Format auch ein wenig anders:
[pytest]
addopts =
--strict-markers
--strict-config
-ra
testpaths = tests
markers =
smoke: Small subset of all tests
exception: Only run expected exceptions
setup.cfg¶
Das Dateiformat der setup.cfg entspricht einer .ini-Datei:
[tool:pytest]
addopts =
--strict-markers
--strict-config
-ra
testpaths = tests
markers =
smoke: Small subset of all tests
exception: Only run expected exceptions
Der einzige Unterschied zwischen dieser und der pytest.ini ist die
Angabe des Abschnitts [tool:pytest].
Warnung
Der Parser der .cfg-Datei unterscheidet sich jedoch vom Parser der
.ini-Datei, und dieser Unterschied kann Probleme verursachen, die
schwer aufzuspüren sind, s.a. pytest-Dokumentation.
rootdir festlegen¶
Noch bevor pytest nach auszuführenden Testdateien sucht, liest es die
Konfigurationsdatei pytest.ini, tox.ini, pyproject.toml
oder setup.cfg, die einen pytest-Abschnitt enthält:
wenn ihr ein Testverzeichnis angegeben habt, beginnt pytest dort zu suchen
wenn ihr mehrere Dateien oder Verzeichnisse angegeben habt, beginnt pytest mit dem übergeordneten Verzeichnis
wenn ihr keine Datei oder kein Verzeichnis angebt, beginnt pytest im aktuellen Verzeichnis.
Wenn pytest eine Konfigurationsdatei im Startverzeichnis findet, ist das die
Wurzel und wenn nicht, geht pytest den Verzeichnisbaum hoch, bis es eine
Konfigurationsdatei findet, die einen pytest-Abschnitt enthält. Sobald pytest
eine Konfigurationsdatei gefunden hat, markiert es das Verzeichnis, in dem es
sie gefunden hat, als rootdir. Dieses Wurzelverzeichnis ist auch die
relative Wurzel der IDs. pytest sagt euch auch, wo es eine Konfigurationsdatei
gefunden hat. Durch diese Regeln können wir Tests auf verschiedenen Ebenen
durchführen und sicher sein, dass pytest die richtige Konfigurationsdatei
findet:
$ cd cusy.tasks
$ uv run pytest
============================= test session starts ==============================
…
rootdir: /Users/veit/cusy/prj/cusy.tasks
configfile: pyproject.toml
testpaths: tests
plugins: Faker-19.11.0
collected 39 items
…
conftest.py für die gemeinsame Nutzung von lokalen Fixtures und Hook-Funktionen¶
Die conftest.py-Datei wird verwendet, um Fixtures und Hook-Funktionen zu
speichern, s.a. Test-Fixtures und Plugins. Ihr
könnt so viele conftest.py-Dateien in einem Projekt haben, wie ihr
wollt. Alles, was in einer conftest.py-Datei definiert ist, gilt für
Tests in diesem Verzeichnis und allen Unterverzeichnissen.
Wenn ihr eine conftest.py-Datei auf der obersten Testebene habt, können
die dort definierten Fixtures für alle Tests verwendet werden. Wenn es dann
spezielle Fixtures gibt, die nur für ein Unterverzeichnis gelten, können diese
in einer anderen conftest.py-Datei in diesem Unterverzeichnis definiert
werden. Zum Beispiel könnten die CLI-Tests andere Fixtures benötigen als die
API-Tests, und einige könnt ihr auch gemeinsam nutzen.
Tipp
Es ist jedoch eine gute Idee, nur eine einzige conftest.py-Datei zu
halten, damit ihr die Fixture-Definitionen leicht finden können. Auch wenn
wir mit pytest --fixtures -v immer herausfinden können, wo eine
Fixture definiert ist, so ist es dennoch einfacher, wenn sie immer in der
einen conftest.py-Datei definiert ist.
__init__.py um Kollision von Testdateinamen zu vermeiden¶
Die __init__.py-Datei erlaubt es, doppelte Testdateinamen zu haben. Wenn
ihr __init__.py-Dateien in jedem Test-Unterverzeichnis habt, könnt ihr
denselben Testdateinamen in mehreren Verzeichnissen verwenden, z.B.:
cusy.tasks
├── …
├── src
│ └── …
└── tests
├── api
│ ├── __init__.py
│ └── test_add.py
├── cli
│ ├── __init__.py
│ ├── conftest.py
│ └── test_add.py
└── conftest.py
Nun können wir die add-Funktionalität sowohl über die API als auch über die CLI testen, wobei eine test_add.py
in beiden Verzeichnissen liegt:
$ uv run pytest
============================= test session starts ==============================
…
rootdir: /Users/veit/cusy/prj/cusy.tasks
configfile: pyproject.toml
testpaths: tests
plugins: Faker-19.11.0
collected 6 items
tests/api/test_add.py .... [ 66%]
tests/cli/test_add.py .. [100%]
============================== 6 passed in 0.03s ===============================
Die meisten meiner Projekte starten mit folgender Konfiguration:
addopts =
--strict-markers
--strict-config
-ra
Siehe auch