Im Bereich des automatischen Testens finden Sie verschiedene Tools. Beispielsweise ist py.test eine der beliebtesten Lösungen zum Schreiben von automatischen Tests in Python.
Nachdem ich viele Ressourcen im Zusammenhang mit pytest durchgesehen und die Dokumentation auf der offiziellen Website des Projekts studiert hatte, konnte ich keine direkte Beschreibung der Lösung für eine der Hauptaufgaben finden - das Ausführen von Tests mit Testdaten, die in einer separaten Datei gespeichert sind. Ansonsten kann gesagt werden, dass Parameter aus der Datei (en) in Testfunktionen geladen oder direkt aus der Datei parametrisiert werden. Ein solches Verfahren wird nirgendwo in den Feinheiten beschrieben, und die einzige Erwähnung dieses Merkmals findet sich nur in einer Zeile der Pytest-Dokumentation.
In diesem Artikel werde ich über meine Lösung für dieses Problem sprechen.
Herausforderung
Die Hauptaufgabe besteht darin, aus den entsprechenden test_input
Testfälle in Form der Parameter test_input
und expected_result
in jede einzelne Testfunktion zu generieren.
Zusätzliche Aufgaben:
- Wählen Sie eine für Menschen lesbare Formatierung von Dateien mit Testfällen.
- Lassen Sie die Fähigkeit, fest codierte Testfälle zu unterstützen.
- Zeigen Sie fĂĽr jeden Fall eindeutige Kennungen an.
Toolkit
In diesem Artikel verwende ich Python 3 (2.7 ist ebenfalls geeignet), Pyyaml ​​und pytest
(Versionen 5+ für Python 3 oder 4.6 für Python 2.7), ohne Plugins von Drittanbietern zu verwenden. Zusätzlich wird die Standard- os
verwendet.
Die Datei selbst, aus der wir Testfälle entnehmen, muss mit einer Markup-Sprache strukturiert werden, die für eine Person bequem zu verstehen ist. In meinem Fall wurde YAML ausgewählt (weil es die zusätzliche Aufgabe der Auswahl eines für Menschen lesbaren Formats löst) . Welche Art von Auszeichnungssprache für Dateien mit Datensätzen Sie benötigen, hängt von den Anforderungen ab, die an das Projekt gestellt werden.
Implementierung
Da die Hauptsäule des Universums bei der Programmierung die Übereinstimmung ist, müssen wir für unsere Lösung mehrere davon einführen.
Abfangen
Diese Lösung verwendet zunächst die pytest_generate_tests
( wiki ), die in der Phase der Generierung von Testfällen beginnt, und das Argument metafunc
, mit dem wir die Funktion parametrisieren können. Zu diesem Zeitpunkt durchläuft pytest jede Testfunktion und führt den nachfolgenden Generierungscode dafür aus.
Argumente
Sie müssen eine vollständige Liste von Parametern für Testfunktionen definieren. In meinem Fall ist das Wörterbuch test_input
und jeder Datentyp (meistens eine Zeichenfolge oder eine Ganzzahl) in expected_result
. Wir benötigen diese Parameter zur Verwendung in metafunc.parametrize(...)
.
Parametrierung
Diese Funktion wiederholt die Operation der Parametrisierungsvorrichtung @pytest.mark.parametrize
vollständig. Als erstes Argument wird eine Zeichenfolge verwendet, in der die Argumente der Testfunktion (in unserem Fall "test_input, expected_result"
) und eine Liste von Daten aufgeführt sind, mit denen die Testfälle erstellt werden (z. B. [(1, 2), (2, 4), (3, 6)]
).
Im Kampf wird es so aussehen:
@pytest.mark.parametrize("test_input, expected_result", [(1, 2), (2, 4), (3, 6)]) def test_multiplication(test_input, expected_result): assert test_input * 2 == expected_result
Und in unserem Fall werden wir dies im Voraus angeben:
Filtern
Ab hier folgt auch die Zuordnung der Testfunktionen, fĂĽr die Daten aus einer Datei erforderlich sind, von denen, die statische / dynamische Daten verwenden. Wir werden diese Filterung anwenden, bevor wir die Informationen aus der Datei analysieren.
Die Filter selbst können beliebig sein, zum Beispiel:
- Funktionsmarker namens
yaml
:
Andernfalls kann derselbe Filter wie folgt implementiert werden:
- Das Argument fĂĽr die Funktion
test_input
:
Diese Option hat mir am besten gefallen.
Ergebnis
Wir mĂĽssen nur den Teil hinzufĂĽgen, in dem wir die Daten aus der Datei analysieren. Dies wird im Fall von Yaml (sowie von JSON , XML usw.) nicht schwierig sein, daher sammeln wir alles auf dem Haufen.
Wir schreiben ein Testskript wie folgt:
Eine Datendatei:
# test_multiplication.yaml - !!python/tuple [1,2] - !!python/tuple [1,3] - !!python/tuple [1,5] - !!python/tuple [2,4] - !!python/tuple [3,4] - !!python/tuple [5,4]
Wir erhalten folgende Liste von Testfällen:
pytest /test_script.py --collect-only ======================== test session starts ======================== platform linux -- Python 3.7.4, pytest-5.2.1, py-1.8.0, pluggy-0.13.0 rootdir: /pytest_habr collected 6 items <Module test_script.py> <Function test_multiplication[1-2]> <Function test_multiplication[1-3]> <Function test_multiplication[1-5]> <Function test_multiplication[2-4]> <Function test_multiplication[3-4]> <Function test_multiplication[5-4]> ======================== no tests ran in 0.04s ========================
Und durch AusfĂĽhren des Skripts ergibt sich folgendes Ergebnis: 4 failed, 2 passed, 1 warnings in 0.11s
HinzufĂĽgen. Aufgaben
Dies könnte das Ende des Artikels sein, aber für die größere Komplexität werde ich unserer Funktion bequemere Bezeichner hinzufügen, eine weitere Datenanalyse und Markierung für jeden einzelnen Testfall.
Also sofort der Code:
Dementsprechend ändern wir, wie unsere YAML-Datei aussehen wird:
# test_multiplication.yaml - test_data: [1, 2] id: 'one_two' - test_data: [1,3] marks: ['xfail'] - test_data: [1,5] marks: ['skip'] - test_data: [2,4] id: "it's good" marks: ['xfail'] - test_data: [3,4] marks: ['negative'] - test_data: [5,4] marks: ['more_than']
Dann ändert sich die Beschreibung zu:
<Module test_script.py> <Function test_multiplication[one_two]> <Function test_multiplication[1_3]> <Function test_multiplication[1_5]> <Function test_multiplication[it's good]> <Function test_multiplication[3_4]> <Function test_multiplication[5_4]>
Und der Start wird sein: 2 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 2 warnings in 0.12s
PS: Warnungen - weil Selbstgeschriebene Marker werden nicht in pytest.ini aufgezeichnet
In der Entwicklung des Themas
Bereit, in den Kommentaren Fragen zum Typ zu diskutieren:
- Was ist der beste Weg, um eine Yaml-Datei zu schreiben?
- In welchem ​​Format ist es bequemer, Testdaten zu speichern?
- Welcher zusätzliche Testfall wird in der Generierungsphase benötigt?
- Benötige ich für jeden Fall Kennungen?