Übung
In dieser Übung verwenden Sie Pytest, um eine Funktion zu testen. Anschließend finden und beheben Sie einige potenzielle Probleme mit der Funktion, die zu fehlerhaften Tests führen. Beim Betrachten von Fehlern und dem Verwenden der umfassenden Fehlerberichterstattung von Pytest ist es wichtig, problematische Tests oder Fehler im Produktionscode zu identifizieren und zu beheben.
Für diese Übung verwenden wir eine Funktion namens admin_command(), die einen Systembefehl als Eingabe entgegennimmt und optional mithilfe des sudo-Tools mit einem Präfix versieht. Die Funktion weist einen Fehler auf, den Sie durch Schreiben von Tests ermitteln.
Schritt 1: Hinzufügen einer Datei mit Tests für diese Übung
Erstellen Sie eine neue Testdatei, indem Sie die Python-Konventionen für Dateinamen für Testdateien befolgen. Nennen Sie die Testdatei test_exercise.py, und fügen Sie den folgenden Code hinzu:
def admin_command(command, sudo=True): """ Prefix a command with `sudo` unless it is explicitly not needed. Expects `command` to be a list. """ if sudo: ["sudo"] + command return commandDie Funktion
admin_command()verwendet eine Liste als Eingabe mithilfe descommand-Arguments und kann optional die Liste mit dem Präfixsudoversehen. Wenn dassudo-Schlüsselwortargument aufFalsefestgelegt ist, gibt es denselben Befehl zurück, der als Eingabe angegeben wird.Fügen Sie in derselben Datei die Tests für die Funktion
admin_command()an. Die Tests verwenden eine Hilfsmethode, die einen Beispielbefehl zurückgibt:class TestAdminCommand: def command(self): return ["ps", "aux"] def test_no_sudo(self): result = admin_command(self.command(), sudo=False) assert result == self.command() def test_sudo(self): result = admin_command(self.command(), sudo=True) expected = ["sudo"] + self.command() assert result == expected
Hinweis
Es ist nicht üblich, Tests innerhalb derselben Datei wie tatsächlichen Code aufzubewahren. Der Einfachheit halber weisen die Beispiele in dieser Übung tatsächlichen Code in derselben Datei auf. In realen Python-Projekten werden Tests in der Regel durch Dateien und Verzeichnisse vom Code getrennt, den sie testen.
Schritt 2: Ausführen der Tests und Ermitteln des Fehlers
Da die Testdatei nun eine Funktion zum Testen und eine Reihe von Tests zur Überprüfung ihres Verhaltens enthält, können Sie die Tests nun ausführen und mit Fehlern arbeiten.
Führen Sie die Datei mit Python aus:
$ pytest test_exercise.pyDer Lauf sollte mit einem bestandenen und einem fehlgeschlagenen Test abgeschlossen werden, und die Fehlerausgabe sollte ungefähr so aussehen wie die folgende Ausgabe:
=================================== FAILURES =================================== __________________________ TestAdminCommand.test_sudo __________________________ self = <test_exercise.TestAdminCommand object at 0x10634c2e0> def test_sudo(self): result = admin_command(self.command(), sudo=True) expected = ["sudo"] + self.command() > assert result == expected E AssertionError: assert ['ps', 'aux'] == ['sudo', 'ps', 'aux'] E At index 0 diff: 'ps' != 'sudo' E Right contains one more item: 'aux' E Use -v to get the full diff test_exercise.py:24: AssertionError =========================== short test summary info ============================ FAILED test_exercise.py::TestAdminCommand::test_sudo - AssertionError: assert... ========================= 1 failed, 1 passed in 0.04s ==========================Fehler in der Ausgabe beim
test_sudo()-Test. Pytest gibt Details zu den beiden verglichenen Listen an. In diesem Fall enthält dieresult-Variable keinensudo-Befehl. Das ist das erwartete Ergebnis des Tests.
Schritt 3: Beheben des Fehlers und erfolgreiche Durchführung der Tests
Bevor Sie Änderungen vornehmen, müssen Sie verstehen, warum ein Fehler auftritt. Auch wenn Sie sehen können, dass die Erwartung nicht erfüllt wird (sudo ist nicht im Ergebnis enthalten), müssen Sie die Ursache herausfinden.
Sehen Sie sich die folgenden Codezeilen aus der admin_command()-Funktion an, wenn die sudo=True-Bedingung erfüllt ist:
if sudo:
["sudo"] + command
Der Vorgang der Listen wird nicht verwendet, um den Wert zurückzugeben. Da er nicht zurückgegeben wird, gibt die Funktion den Befehl letztlich stets ohne sudo zurück.
Aktualisieren Sie die
admin_command()-Funktion, damit der Listenvorgang zurückgegeben wird, sodass das geänderte Ergebnis beim Anfordern einessudo-Befehls verwendet wird. Die aktualisierte Funktion sollte wie folgt aussehen:def admin_command(command, sudo=True): """ Prefix a command with `sudo` unless it is explicitly not needed. Expects `command` to be a list. """ if sudo: return ["sudo"] + command return commandFühren Sie den Test mit Pytest erneut aus. Versuchen Sie, die Ausführlichkeit der Ausgabe zu verbessern, indem Sie das
-v-Flag mit Pytest verwenden:$ pytest -v test_exercise.pyÜberprüfen Sie nun die Ausgabe. Nun sollten zwei bestandene Tests gezeigt werden:
============================= test session starts ============================== Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 cachedir: .pytest_cache rootdir: /private collected 2 items test_exercise.py::TestAdminCommand::test_no_sudo PASSED [ 50%] test_exercise.py::TestAdminCommand::test_sudo PASSED [100%] ============================== 2 passed in 0.00s ===============================
Hinweis
Weil die Funktion mit mehreren Werten verschiedener Schreibung arbeiten kann, sollten weitere Tests hinzugefügt werden, um diese Variationen abzudecken. Dadurch kann in Zukunft verhindert werden, dass Änderungen an der Funktion ein anderes (unerwartetes) Verhalten hervorrufen.
Schritt 4: Hinzufügen von neuem Code mit Tests
Nach dem Hinzufügen von Tests in den vorherigen Schritten sollten Sie sich wohl fühlen, weitere Änderungen an der Funktion vorzunehmen und sie mit Tests zu überprüfen. Selbst wenn die Änderungen nicht durch vorhandene Tests abgedeckt sind, können Sie sicher sein, dass Sie gegen keine vorherigen Annahmen verstoßen.
In diesem Fall vertraut die admin_command()-Funktion blind darauf, dass das command-Argument stets eine Liste ist. Wir können dies verbessern, indem wir sicherstellen, dass eine Ausnahme mit einer hilfreichen Fehlermeldung ausgelöst wird.
Erstellen Sie zunächst einen Test, der das Verhalten erfasst. Obwohl die Funktion noch nicht aktualisiert wird, versuchen Sie es mit einem Test-first-Ansatz (auch bekannt als Test Driven Development oder TDD).
- Aktualisieren Sie die Datei test_exercise.py, damit sie
pytestoben importiert. Dieser Test verwendet ein internes Hilfsprogramm aus dempytest-Framework:
import pytest- Fügen Sie nun einen neuen Test an die Klasse an, um die Ausnahme zu überprüfen. Dieser Test sollte eine
TypeErrorvon der Funktion erwarten, wenn der an sie übergebene Wert keine Liste ist:
def test_non_list_commands(self): with pytest.raises(TypeError): admin_command("some command", sudo=True)- Aktualisieren Sie die Datei test_exercise.py, damit sie
Führen Sie die Tests erneut mit Pytest aus. Sie sollten alle bestehen:
============================= test session starts ============================== Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /private/ collected 3 items test_exercise.py ... [100%] ============================== 3 passed in 0.00s ===============================Der Test ist gut genug für eine Überprüfung von
TypeError. Es wäre allerdings gut, den Code mit einer hilfreichen Fehlermeldung hinzuzufügen.Aktualisieren Sie die Funktion, um
TypeErrorexplizit mit einer hilfreichen Fehlermeldung auszulösen:def admin_command(command, sudo=True): """ Prefix a command with `sudo` unless it is explicitly not needed. Expects `command` to be a list. """ if not isinstance(command, list): raise TypeError(f"was expecting command to be a list, but got a {type(command)}") if sudo: return ["sudo"] + command return commandAktualisieren Sie schließlich die
test_non_list_commands()-Methode, um nach der Fehlermeldung zu suchen:def test_non_list_commands(self): with pytest.raises(TypeError) as error: admin_command("some command", sudo=True) assert error.value.args[0] == "was expecting command to be a list, but got a <class 'str'>"Der aktualisierte Test verwendet
errorals Variable, die alle Ausnahmeinformationen enthält. Mithilfe vonerror.value.argskönnen Sie die Argumente der Ausnahme untersuchen. In diesem Fall weist das erste Argument die Fehlerzeichenfolge auf, die der Test überprüfen kann.
Überprüfen Ihrer Arbeit
Jetzt sollten Sie eine Python-Testdatei namens test_exercise.py haben, die Folgendes enthält:
- Eine
admin_command()-Funktion, die ein Argument akzeptiert, und ein Schlüsselwortargument - Eine
TypeError-Ausnahme mit einer hilfreichen Fehlermeldung in deradmin_command()-Funktion - Eine
TestAdminCommand()-Testklasse mit einercommand()-Hilfsmethode und drei Testmethoden, die dieadmin_command()-Funktion überprüfen
Alle Tests sollten ohne Fehler bestehen, wenn Sie sie im Terminal ausführen.