j4n
j4n.e7h.eu
e7h

2026-10-13 16:00:00

pytest-csv-params: Testfall-Daten als »smarte Objekte«

Eigentlich sollte die Version 2 meines Pytest1-Plugins pytest-csv-params keine neuen Features mehr bekommen, da einige Ideen, die ich noch habe, sich nicht mit den derzeit unterstützten Pytest- und Python-Versionen realisieren lassen. Entsprechend sollten sie erst mit dem nächsten Major-Release, welches als »Breaking Change« die Einstellung des Supports für Python 3.10 und Pytest kleiner als 9 mitbringt, erscheinen.

Doch die Bereitstellung von Testfall-Daten als »smarte Objekte« lässt sich problemlos mit Python 3.10 und Pytest 8.4 realisieren. Und so ist die Funktion heute in die Version 2.3.0 eingeflossen.

Worum geht es?

Ähnlich der Bereitstellung von Testdaten als Dictionary2, ermöglicht die neue Funktion die Nutzung von Objekten zur Übergabe. Doch nicht nur das: Objekte lassen sich durch selbstdefinierte Factories erzeugen. Dieser Beitrag zeigt einige Beispiele und Use Cases.

Die Dokumentation der neuen Konfigurationsmöglichkeiten befindet sich an gewohnter Stelle .

`parameters_as_object` vs. `parameters_as_dictionary`

Die gleichzeitige Nutzung der beiden Optionen parameters_as_object bzw. parameters_as_dictionary ist nicht möglich und führt zu einer entsprechenden Exception.

Zwei grundsätzliche Konfigurationsmöglichkeiten

Für den Decorator-Parameter parameters_as_object gibt es zwei Konfigurationsmöglichkeiten:

  • Einfach: Wert vom Typ str – so wird einfach nur der Name des Parameters definiert, an den das Objekt übergeben werden soll. Das entspricht weitestgehend dem Verhalten, welches man bereits vom Parameter parameters_as_dictionary gewohnt ist.
  • Erweitert: Wert vom Typ tuple[str, TestCaseDataObjectFactory] – damit wird zum einen der Name (wie oben) übergeben, zum anderen eine Factory-Methode vom Typ TestCaseDataObjectFactory3, die neue Objekte vom Typ TestCaseDataElementType4 erzeugt.

Die einfache Konfiguration

Bei der einfachen Konfiguration wird einfach nur der Name des Testfunktionsarguments übergeben. Der Name muss vom Typ str sein. Ansonsten erfolgt eine entsprechende Exception.

 1from typing import Any
 2from pytest_csv_params.decorator import csv_params
 3
 4
 5@csv_params(
 6    data_file="/data/test-lib/cases/texts.csv",
 7    parameters_as_object="test_data",
 8)
 9def test_texts(test_data: Any):
10    assert f"{test_data.text_a}:{test_data.text_b}" == test_data.text_c

Grundsätzlich können – wie auch bei der Nutzung von parameters_as_dictionary – alle weiteren Funktionen, wie Data Casts5 oder Feldumbenennungen6 verwendet werden. Um das Beispiel einfach zu halten, wurde in diesem Beispiel jedoch darauf verzichtet.

Die erweiterte Konfiguration

Die erweiterte Konfiguration ermöglicht die Verwendung von eigenen Factory-Methoden für die Erzeugung der Objekte, an denen die entsprechenden Werte registriert werden.

Die Factory-Methoden müssen Objekte liefern, die eine Instanz-Methode register_value implementieren, die zwei Parameter key (Typ str) und value (Typ Any) akzeptiert. Diese Methode wird während der Parametrisierung verwendet. So kann man allerlei eigene Anpassungen vornehmen. Das folgende Beispiel gibt einen Einblick anhand dieser CSV-Datei:

1"#ID", "Animal", "Color"
2"A1", "Cat", "Blue"
3"A2", "Dog", "Green"
4"A3", "Mouse", "Yellow"
5"A4", "Bird", "Purple"
6"A5", "Cow", "Red"

Der folgende Python-Code definiert nicht nur den Test, sondern auch noch den Umgang mit Testdaten aus der CSV-Datei. So sollen zum Beispiel String-Werte umgekehrt und in Großbuchstaben gewandelt werden, während die ID des Testfalls sowie andere Datentypen unberührt bleiben. Außerdem soll jedem Objekt eine eindeutige UUID zugeordnet werden. Als Factory-Methode wird direkt die Klasse TestDataObject verwendet:

 1from os.path import join, dirname
 2from typing import Any
 3from uuid import uuid4, UUID
 4
 5from pytest_csv_params.decorator import csv_params
 6from pytest_csv_params.object import TestCaseDataElementType
 7
 8
 9class TestDataObject(TestCaseDataElementType):
10
11    def register_value(self, key: str, value: Any) -> None:
12        if value is not None and isinstance(value, str) and key != "tc_id":
13            setattr(self, key, (value[::-1]).upper())
14            return
15        setattr(self, key, value)
16
17    def __init__(self) -> None:
18        self._uuid = uuid4()
19        self.animal: str | None = None
20        self.color: str | None = None
21        self.tc_id: str | None = None
22
23
24@csv_params(
25    data_file=join(dirname(__file__), "test_data", "test1.csv"),
26    id_col="#ID",
27    reuse_id_col=True,
28    header_renames={
29        "#ID": "tc_id",
30        "Color": "color",
31        "Animal": "animal",
32    },
33    parameters_as_object=("tc_data_object", TestDataObject),
34)
35def test_with_object(tc_data_object: TestDataObject) -> None:
36    assert isinstance(tc_data_object._uuid, UUID)
37    assert tc_data_object.tc_id in ("A1", "A2", "A3", "A4", "A5")
38    assert isinstance(tc_data_object.color, str)
39    assert isinstance(tc_data_object.animal, str)

Entsprechend kommen in der Methode test_with_object nun solche Objekte an (hier im Beispiel für den ersten Testfall aus der CSV-Datei):

Test-Objekt im Debugger

Die zusätzlichen Felder, Umbenennungen und Werte-Anpassungen sind gut zu erkennen.

Fazit

Die Nutzung von parameters_as_object gerade in Verbindung mit dem Konfigurations-Tuple macht das Plugin pytest-csv-params noch mächtiger. Durch die Möglichkeit, Klassen entsprechend zu definieren, kann man weit besser von der eigenen IDE unterstützt werden, so dass bei Feldnamen werden Tippfehler vermieden werden können und man komfortabler arbeiten kann.

Dieses Verhalten hat durchaus das Potenzial, für Version 3 die Standard-Variante zu werden.


Hinweis zur KI-Nutzung

Dieser Artikel wurde von einem Menschen geschrieben, anschließend mit Hilfe von KI korrigiert und teilweise umformuliert.

KI hat keinerlei Inhalte selbst erzeugt oder Fakten beigetragen.


  1. Pytest ist ein sehr bekanntes und weit verbreitetes Test-Framework für Python. Siehe docs.pytest.org/en/stable/ . ↩︎

  2. siehe Plugin-Dokumentation zum Decorator-Parameter parameters_as_dictionary  ↩︎

  3. Der Typ TestCaseDataObjectFactory entspricht einer Signatur von Callable[[], TestCaseDataElementType] ↩︎

  4. Die von der Factory erzeugte Objektinstanzen müssen eine Methode register_value(self, key: str, value: Any) implementieren ↩︎

  5. Siehe Decorator-Parameter data_casts  ↩︎

  6. Siehe Decorator-Parameter header_renames  ↩︎




Zuletzt geändert: 2026-10-13 21:17:12