Erstelle einige automatisierte Tests

DjangoDjangoBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

Dieser Tutorial beginnt dort, wo **Form Processing and Cutting Down Our Code** aufgehört hat. Wir haben eine Weboberflächen-Umfrageanwendung gebaut und werden nun einige automatisierte Tests für sie erstellen.

Einführung in die automatisierte Tests

Was sind automatisierte Tests?

Tests sind Routinen, die die Funktionsweise Ihres Codes überprüfen.

Das Testen erfolgt auf verschiedenen Ebenen. Einige Tests können sich auf einen winzigen Detailbereich beziehen („gibt eine bestimmte Modellmethode die erwarteten Werte zurück?“), während andere die Gesamtfunktionsweise der Software untersuchen („erzeugt eine Sequenz von Benutzereingaben auf der Website das gewünschte Ergebnis?“). Dies unterscheidet sich nicht von der Art des Tests, die Sie früher in **Set Up the Database** durchgeführt haben, indem Sie die shell verwendet haben, um das Verhalten einer Methode zu untersuchen, oder indem Sie die Anwendung ausgeführt und Daten eingegeben haben, um zu überprüfen, wie sie sich verhält.

Was bei automatisierten Tests anders ist, ist, dass die Testarbeit vom System für Sie erledigt wird. Sie erstellen einmal eine Reihe von Tests, und wenn Sie Ihre App ändern, können Sie überprüfen, ob Ihr Code weiterhin wie ursprünglich geplant funktioniert, ohne dass Sie zeitaufwendiges manuelles Testen vornehmen müssen.

Warum Sie Tests erstellen sollten

Warum sollten Sie Tests erstellen und warum gerade jetzt?

Sie mögen sich vielleicht denken, dass Sie schon genug auf dem Teller haben, indem Sie nur noch Python/Django lernen, und dass das Lernen und Tun noch eines weiteren Dings überwältigend und vielleicht sogar unnötig erscheint. Nach allem, was wir wissen, funktioniert unsere Umfrageanwendung derzeit problemlos; das Aufwand der Erstellung automatisierter Tests wird sie nicht funktionieren lassen. Wenn das Erstellen der Umfrageanwendung der letzte Schritt der Django-Programmierung ist, die Sie jemals ausführen werden, dann stimmt es, dass Sie nicht wissen müssen, wie automatisierte Tests erstellt werden. Wenn das jedoch nicht der Fall ist, ist jetzt ein ausgezeichneter Zeitpunkt, um zu lernen.

Tests sparen Ihnen Zeit

Bis zu einem gewissen Punkt wird das „Überprüfen, ob es funktioniert“ ein zufriedenstellender Test sein. In einer komplexeren Anwendung können Sie zwischen den Komponenten zahlreiche komplexe Interaktionen haben.

Eine Änderung in einer dieser Komponenten kann unerwartete Auswirkungen auf das Verhalten der Anwendung haben. Das Überprüfen, ob es weiterhin „funktioniert“, kann bedeuten, dass Sie durch die Funktionalität Ihres Codes mit zwanzig verschiedenen Variationen Ihrer Testdaten laufen, um sicherzustellen, dass Sie nichts kaputt gemacht haben – kein guter Zeitverbrauch.

Dies trifft besonders zu, wenn automatisierte Tests dies für Sie in wenigen Sekunden erledigen können. Wenn etwas schiefgeht, helfen Ihnen Tests auch bei der Identifizierung des Codes, der das unerwartete Verhalten verursacht.

Manchmal kann es sich anfühlen, als wäre es eine Last, sich von Ihrer produktiven, kreativen Programmierarbeit zu lösen, um der langweiligen und unaufregenden Aufgabe der Schreiben von Tests zu begegnen, insbesondere wenn Sie wissen, dass Ihr Code ordnungsgemäß funktioniert.

Dennoch ist die Aufgabe des Schreibens von Tests viel erfüllender als Stunden am manuellen Testen Ihrer Anwendung oder am Versuch, die Ursache eines neu aufgetretenen Problems zu identifizieren.

Tests identifizieren nicht nur Probleme, sondern verhindern sie auch

Es ist ein Fehler, Tests lediglich als einen negativen Aspekt der Entwicklung zu betrachten.

Ohne Tests kann der Zweck oder das beabsichtigte Verhalten einer Anwendung ziemlich undurchsichtig sein. Selbst wenn es Ihr eigener Code ist, werden Sie sich manchmal darin herumtappen, um herauszufinden, was er genau macht.

Tests ändern das; sie beleuchten Ihren Code von innen heraus, und wenn etwas schiefgeht, richten sie das Licht auf den Teil, der fehlerhaft ist – auch wenn Sie nicht einmal bemerkt haben, dass etwas schiefgeht.

Tests machen Ihren Code attraktiver

Sie könnten ein brillantes Softwarestück erstellt haben, aber Sie werden feststellen, dass viele andere Entwickler es ablehnen, weil es keine Tests hat; ohne Tests vertrauen sie es nicht. Jacob Kaplan-Moss, einer der ursprünglichen Django-Entwickler, sagt: „Code ohne Tests ist von der Design her fehlerhaft.“

Der Grund, warum andere Entwickler Tests in Ihrer Software sehen möchten, bevor sie es ernst nehmen, ist noch ein weiterer Grund für Sie, anfangen, Tests zu schreiben.

Tests helfen Teams zusammenzuarbeiten

Die vorherigen Punkte sind aus der Sicht eines einzelnen Entwicklers geschrieben, der eine Anwendung unterhält. Komplexe Anwendungen werden von Teams unterhalten. Tests gewährleisten, dass Kollegen Ihren Code nicht versehentlich kaputt machen (und dass Sie auch nicht den ihren ohne zu wissen). Wenn Sie als Django-Programmierer leben möchten, müssen Sie gut im Schreiben von Tests sein!

Grundlegende Teststrategien

Es gibt viele Wege, um Tests zu schreiben.

Einige Programmierer folgen einer Disziplin namens "testgetriebene Entwicklung"; sie schreiben tatsächlich ihre Tests, bevor sie ihren Code schreiben. Dies mag zunächst gegenintuitive erscheinen, ist aber tatsächlich ähnlich dem, was die meisten Menschen sowieso oft tun: Sie beschreiben ein Problem und erstellen dann Code, um es zu lösen. Die testgetriebene Entwicklung formalisieren das Problem in einem Python-Testfall.

Oft werden Anfänger beim Testen zuerst Code erstellen und später entscheiden, dass dieser Tests benötigen sollte. Vielleicht wäre es besser gewesen, Tests früher zu schreiben, aber es ist nie zu spät, um zu beginnen.

Manchmal ist es schwierig, herauszufinden, wo man mit dem Schreiben von Tests beginnen soll. Wenn Sie bereits mehrere tausend Zeilen Python-Code geschrieben haben, kann es nicht einfach sein, etwas auszuwählen, das getestet werden soll. In einem solchen Fall lohnt es sich, Ihren ersten Test beim nächsten Codeänderungsschritt zu schreiben, sei es, wenn Sie eine neue Funktion hinzufügen oder einen Bug beheben.

Lassen Sie uns daher sofort loslegen.

Schreiben unseres ersten Tests

Wir identifizieren einen Bug

Zum Glück gibt es in der Umfrage-Anwendung einen kleinen Bug, den wir sofort beheben können: Die Methode Question.was_published_recently() gibt True zurück, wenn die Question innerhalb des letzten Tages veröffentlicht wurde (was korrekt ist), aber auch wenn das pub_date-Feld der Question in der Zukunft liegt (was sicherlich nicht der Fall ist).

Bestätigen Sie den Bug, indem Sie die shell verwenden, um die Methode für eine Frage zu überprüfen, deren Datum in der Zukunft liegt:

cd ~/project/mysite
python manage.py shell
>>> import datetime
>>> from django.utils import timezone
>>> from polls.models import Question
>>> ## Erstellen Sie eine Question-Instanz mit pub_date 30 Tage in der Zukunft
>>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
>>> ## Wurde sie kürzlich veröffentlicht?
>>> future_question.was_published_recently()
True

Da Dinge in der Zukunft nicht „kürzlich“ sind, ist dies eindeutig falsch.

Erstellen eines Tests, um den Bug aufzudecken

Was wir gerade in der shell getan haben, um das Problem zu testen, können wir genauso in einem automatisierten Test tun. Lassen Sie uns daher das in einen automatisierten Test umwandeln.

Ein üblicher Ort für die Tests einer Anwendung ist die tests.py-Datei der Anwendung; das Testsystem findet automatisch Tests in jeder Datei, deren Name mit test beginnt.

Fügen Sie Folgendes in die tests.py-Datei in der Umfrage-Anwendung hinzu:

import datetime

from django.test import TestCase
from django.utils import timezone

from.models import Question


class QuestionModelTests(TestCase):
    def test_was_published_recently_with_future_question(self):
        """
        was_published_recently() gibt False für Fragen zurück, deren pub_date
        in der Zukunft liegt.
        """
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertIs(future_question.was_published_recently(), False)

Hier haben wir eine Unterklasse von django.test.TestCase erstellt, die eine Methode enthält, die eine Question-Instanz mit einem pub_date in der Zukunft erstellt. Wir überprüfen dann die Ausgabe von was_published_recently() – die sollte False sein.

Ausführen von Tests

Im Terminal können wir unseren Test ausführen:

python manage.py test polls

und Sie werden etwas wie Folgendes sehen:

[object Object]

Anderer Fehler?

Wenn Sie hier stattdessen einen NameError erhalten, haben Sie vielleicht einen Schritt in Teil 2 <tutorial02-import-timezone> übersehen, in dem wir die Imports von datetime und timezone in polls/models.py hinzugefügt haben. Kopieren Sie die Imports aus diesem Abschnitt und versuchen Sie, Ihre Tests erneut auszuführen.

Was passiert ist Folgendes:

  • manage.py test polls suchte nach Tests in der Umfrage-Anwendung
  • es fand eine Unterklasse der django.test.TestCase-Klasse
  • es erstellte eine spezielle Datenbank zum Zwecke des Tests
  • es suchte nach Testmethoden – solchen, deren Namen mit test beginnen
  • in test_was_published_recently_with_future_question erstellte es eine Question-Instanz, deren pub_date-Feld 30 Tage in der Zukunft ist
    -... und mit der assertIs()-Methode stellte es fest, dass ihre was_published_recently() True zurückgibt, obwohl wir wollten, dass sie False zurückgibt

Der Test informiert uns, welcher Test fehlgeschlagen ist und sogar die Zeile, an der der Fehler aufgetreten ist.

Beheben des Bugs

Wir wissen bereits, was das Problem ist: Question.was_published_recently() sollte False zurückgeben, wenn sein pub_date in der Zukunft liegt. Ändern Sie die Methode in models.py, sodass sie nur True zurückgibt, wenn das Datum auch in der Vergangenheit liegt:

def was_published_recently(self):
    now = timezone.now()
    return now - datetime.timedelta(days=1) <= self.pub_date <= now

und führen Sie den Test erneut aus:

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Destroying test database for alias 'default'...

Nachdem wir einen Bug identifiziert haben, haben wir einen Test geschrieben, der ihn aufdeckt, und den Bug im Code korrigiert, sodass unser Test bestanden wird.

In Zukunft könnten viele andere Dinge mit unserer Anwendung schiefgehen, aber wir können sicher sein, dass wir diesen Bug nicht versehentlich wieder einführen, da das Ausführen des Tests uns sofort warnen wird. Wir können diese kleine Teilmenge der Anwendung als sicher für immer betrachten.

Umfassendere Tests

Während wir hier sind, können wir die Methode was_published_recently() weiter festlegen; tatsächlich wäre es eher peinlich, wenn wir bei der Behebung eines Bugs einen anderen eingeführt hätten.

Fügen Sie zwei weitere Testmethoden zur gleichen Klasse hinzu, um das Verhalten der Methode umfassender zu testen:

def test_was_published_recently_with_old_question(self):
    """
    was_published_recently() gibt False für Fragen zurück, deren pub_date
    älter als 1 Tag ist.
    """
    time = timezone.now() - datetime.timedelta(days=1, seconds=1)
    old_question = Question(pub_date=time)
    self.assertIs(old_question.was_published_recently(), False)


def test_was_published_recently_with_recent_question(self):
    """
    was_published_recently() gibt True für Fragen zurück, deren pub_date
    innerhalb des letzten Tages liegt.
    """
    time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
    recent_question = Question(pub_date=time)
    self.assertIs(recent_question.was_published_recently(), True)

Und jetzt haben wir drei Tests, die bestätigen, dass Question.was_published_recently() sinnvolle Werte für vergangene, aktuelle und zukünftige Fragen zurückgibt.

Wiederum ist Umfrage eine minimale Anwendung, aber wie komplex auch immer sie in Zukunft wird und mit welchen anderen Code sie interagiert, wir haben jetzt die Gewähr, dass die Methode, für die wir Tests geschrieben haben, wie erwartet verhalten wird.

Testen einer Ansicht

Die Umfrageanwendung ist ziemlich uneingeschränkt: Sie wird jede Frage veröffentlichen, einschließlich solcher, deren pub_date-Feld in der Zukunft liegt. Wir sollten dies verbessern. Ein pub_date in der Zukunft sollte bedeuten, dass die Frage zu diesem Zeitpunkt veröffentlicht wird, aber bis dahin unsichtbar.

Ein Test für eine Ansicht

Als wir oben den Bug behebt haben, haben wir zuerst den Test geschrieben und dann den Code, um ihn zu beheben. Tatsächlich war das ein Beispiel für testgetriebene Entwicklung, aber es spielt eigentlich keine Rolle, in welcher Reihenfolge wir die Arbeit erledigen.

In unserem ersten Test haben wir uns eng auf das interne Verhalten des Codes konzentriert. Für diesen Test möchten wir überprüfen, wie es sich verhält, wenn ein Benutzer es über einen Webbrowser erlebt.

Bevor wir versuchen, etwas zu beheben, schauen wir uns die zur Verfügung stehenden Tools an.

Der Django-Testclient

Django stellt einen Testclient ~django.test.Client zur Verfügung, um die Interaktion eines Benutzers mit dem Code auf Ansichts Ebene zu simulieren. Wir können ihn in tests.py oder sogar in der shell verwenden.

Wir beginnen wieder mit der shell, wo wir ein paar Dinge tun müssen, die in tests.py nicht erforderlich sind. Das erste ist, die Testumgebung in der shell einzurichten:

python manage.py shell
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()

~django.test.utils.setup_test_environment installiert einen Template-Renderer, der uns ermöglicht, einige zusätzliche Attribute auf Antworten wie response.context zu untersuchen, die sonst nicht verfügbar wären. Beachten Sie, dass diese Methode nicht eine Testdatenbank einrichtet, sodass die folgenden Befehle gegen die vorhandene Datenbank ausgeführt werden und die Ausgabe je nach den Fragen, die Sie bereits erstellt haben, etwas unterschiedlich aussehen kann. Sie können unerwartete Ergebnisse erhalten, wenn Ihre TIME_ZONE in settings.py nicht korrekt ist. Wenn Sie sich nicht daran erinnern, sie früher eingestellt zu haben, überprüfen Sie sie, bevor Sie fortfahren.

Als nächstes müssen wir die Testclient-Klasse importieren (später in tests.py werden wir die django.test.TestCase-Klasse verwenden, die ihren eigenen Client mitbringt, sodass dies nicht erforderlich sein wird):

>>> from django.test import Client
>>> ## Erstellen Sie eine Instanz des Clients für unseren Gebrauch
>>> client = Client()

Mit diesem eingerichtet können wir dem Client fragen, uns einige Arbeit zu leisten:

>>> ## Holen Sie sich eine Antwort von '/'
>>> response = client.get("/")
Not Found: /
>>> ## Wir sollten von dieser Adresse eine 404 erwarten; wenn Sie stattdessen
>>> ## einen "Invalid HTTP_HOST header"-Fehler und eine 400-Antwort sehen, haben
>>> ## Sie wahrscheinlich den oben beschriebenen Aufruf von setup_test_environment()
>>> ## übersehen.
>>> response.status_code
404
>>> ## Andererseits sollten wir etwas auf '/polls/' finden
>>> ## Wir verwenden'reverse()' anstelle einer hartcodierten URL
>>> from django.urls import reverse
>>> response = client.get(reverse("polls:index"))
>>> response.status_code
200
>>> response.content
b'\n    <ul>\n    \n        <li><a href="/polls/1/">What&#x27;s up?</a></li>\n    \n    </ul>\n\n'
>>> response.context["latest_question_list"]
<QuerySet [<Question: What's up?>]>

Verbesserung unserer Ansicht

Die Liste der Umfragen zeigt Umfragen an, die noch nicht veröffentlicht sind (d.h. die, die ein pub_date in der Zukunft haben). Lassen Sie uns das beheben.

In **Form Processing and Cutting Down Our Code** haben wir eine klassengebasierte Ansicht eingeführt, basierend auf ~django.views.generic.list.ListView:

class IndexView(generic.ListView):
    template_name = "polls/index.html"
    context_object_name = "latest_question_list"

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by("-pub_date")[:5]

Wir müssen die get_queryset()-Methode ändern und so umgestalten, dass sie auch das Datum überprüft, indem es mit timezone.now() verglichen wird. Zunächst müssen wir einen Import hinzufügen:

from django.utils import timezone

und dann müssen wir die get_queryset-Methode wie folgt ändern:

def get_queryset(self):
    """
    Return the last five published questions (not including those set to be
    published in the future).
    """
    return Question.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[
        :5
    ]

Question.objects.filter(pub_date__lte=timezone.now()) gibt einen QuerySet zurück, der Questions enthält, deren pub_date kleiner oder gleich - d.h. früher oder gleich - timezone.now ist.

Testen unserer neuen Ansicht

Jetzt können Sie sich vergewissern, dass dies wie erwartet funktioniert, indem Sie runserver starten, die Website in Ihrem Browser laden, Questions mit Daten in der Vergangenheit und Zukunft erstellen und überprüfen, dass nur die veröffentlichten aufgelistet werden. Sie möchten nicht jedes Mal, wenn Sie eine Änderung vornehmen, die dies möglicherweise beeinflusst, dazu gezwungen sein, dies zu tun - also erstellen wir auch einen Test, basierend auf unserer obigen shell-Session.

Fügen Sie Folgendes zu polls/tests.py hinzu:

from django.urls import reverse

und wir erstellen eine Kurzschreibfunktion, um Fragen zu erstellen, sowie eine neue Testklasse:

def create_question(question_text, days):
    """
    Create a question with the given `question_text` and published the
    given number of `days` offset to now (negative for questions published
    in the past, positive for questions that have yet to be published).
    """
    time = timezone.now() + datetime.timedelta(days=days)
    return Question.objects.create(question_text=question_text, pub_date=time)


class QuestionIndexViewTests(TestCase):
    def test_no_questions(self):
        """
        If no questions exist, an appropriate message is displayed.
        """
        response = self.client.get(reverse("polls:index"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "No polls are available.")
        self.assertQuerySetEqual(response.context["latest_question_list"], [])

    def test_past_question(self):
        """
        Questions with a pub_date in the past are displayed on the
        index page.
        """
        question = create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse("polls:index"))
        self.assertQuerySetEqual(
            response.context["latest_question_list"],
            [question],
        )

    def test_future_question(self):
        """
        Questions with a pub_date in the future aren't displayed on
        the index page.
        """
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse("polls:index"))
        self.assertContains(response, "No polls are available.")
        self.assertQuerySetEqual(response.context["latest_question_list"], [])

    def test_future_question_and_past_question(self):
        """
        Even if both past and future questions exist, only past questions
        are displayed.
        """
        question = create_question(question_text="Past question.", days=-30)
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse("polls:index"))
        self.assertQuerySetEqual(
            response.context["latest_question_list"],
            [question],
        )

    def test_two_past_questions(self):
        """
        The questions index page may display multiple questions.
        """
        question1 = create_question(question_text="Past question 1.", days=-30)
        question2 = create_question(question_text="Past question 2.", days=-5)
        response = self.client.get(reverse("polls:index"))
        self.assertQuerySetEqual(
            response.context["latest_question_list"],
            [question2, question1],
        )

Schauen wir uns einige davon genauer an.

Zunächst ist eine Frage-Kurzschreibfunktion, create_question, um einige Wiederholungen beim Erstellen von Fragen zu vermeiden.

test_no_questions erstellt keine Fragen, sondern überprüft die Nachricht: "No polls are available." und verifiziert, dass die latest_question_list leer ist. Beachten Sie, dass die django.test.TestCase-Klasse einige zusätzliche Assertionsmethoden bietet. In diesen Beispielen verwenden wir ~django.test.SimpleTestCase.assertContains() und ~django.test.TransactionTestCase.assertQuerySetEqual().

In test_past_question erstellen wir eine Frage und überprüfen, dass sie in der Liste erscheint.

In test_future_question erstellen wir eine Frage mit einem pub_date in der Zukunft. Die Datenbank wird für jede Testmethode zurückgesetzt, sodass die erste Frage nicht mehr vorhanden ist, und daher sollte auch der Index keine Fragen enthalten.

Und so weiter. Tatsächlich verwenden wir die Tests, um eine Geschichte über die Admin-Eingabe und die Benutzererfahrung auf der Website zu erzählen und zu überprüfen, dass in jedem Zustand und für jede neue Änderung im Zustand des Systems die erwarteten Ergebnisse veröffentlicht werden.

Testen der DetailView

Was wir haben, funktioniert gut; jedoch können Benutzer, auch wenn zukünftige Fragen nicht im Index erscheinen, immer noch auf sie zugreifen, wenn sie die richtige URL kennen oder erraten. Wir müssen daher eine ähnliche Einschränkung für die DetailView hinzufügen:

class DetailView(generic.DetailView):
  ...

    def get_queryset(self):
        """
        Excludes any questions that aren't published yet.
        """
        return Question.objects.filter(pub_date__lte=timezone.now())

Wir sollten dann einige Tests hinzufügen, um zu überprüfen, dass eine Question, deren pub_date in der Vergangenheit ist, angezeigt werden kann, und dass eine mit einem pub_date in der Zukunft nicht angezeigt wird:

class QuestionDetailViewTests(TestCase):
    def test_future_question(self):
        """
        The detail view of a question with a pub_date in the future
        returns a 404 not found.
        """
        future_question = create_question(question_text="Future question.", days=5)
        url = reverse("polls:detail", args=(future_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_past_question(self):
        """
        The detail view of a question with a pub_date in the past
        displays the question's text.
        """
        past_question = create_question(question_text="Past Question.", days=-5)
        url = reverse("polls:detail", args=(past_question.id,))
        response = self.client.get(url)
        self.assertContains(response, past_question.question_text)

Ideen für weitere Tests

Wir sollten eine ähnliche get_queryset-Methode zu ResultsView hinzufügen und eine neue Testklasse für diese Ansicht erstellen. Es wird sehr ähnlich zu dem, was wir gerade erstellt haben; tatsächlich wird es eine Menge Wiederholungen geben.

Wir könnten unsere Anwendung auch auf andere Weise verbessern und dabei Tests hinzufügen. Beispielsweise ist es dumm, dass Questions auf der Website veröffentlicht werden können, die keine Choices haben. Unsere Ansichten könnten daher darauf prüfen und solche Questions ausschließen. Unsere Tests würden eine Question ohne Choices erstellen und dann testen, dass sie nicht veröffentlicht wird, sowie eine ähnliche Question mit Choices erstellen und testen, dass sie veröffentlicht wird.

Vielleicht sollten angemeldete Admin-Benutzer nicht veröffentlichte Questions sehen können, aber normale Besucher schon. Wieder: Alles, was zur Software hinzugefügt werden muss, um dies zu erreichen, sollte von einem Test begleitet werden, ob Sie den Test zuerst schreiben und dann den Code so gestalten, dass er den Test besteht, oder zuerst die Logik in Ihrem Code erarbeiten und dann einen Test schreiben, um es zu beweisen.

Nach einer gewissen Zeit werden Sie sicherlich auf Ihre Tests schauen und sich fragen, ob Ihr Code unter Testüberflutung leidet, was uns zu:

Beim Testen ist mehr besser

Es mag so aussehen, als würden unsere Tests außer Kontrolle geraten. In diesem Tempo wird es bald mehr Code in unseren Tests geben als in unserer Anwendung, und die Wiederholungen wirken unästhetisch im Vergleich zur eleganten Kürze des restlichen Codes.

Es spielt keine Rolle. Lassen Sie sie wachsen. Großteils können Sie einen Test einmal schreiben und dann vergessen. Er wird seine nützliche Funktion weiterhin erfüllen, während Sie Ihr Programm weiterentwickeln.

Manchmal müssen die Tests aktualisiert werden. Nehmen wir an, dass wir unsere Ansichten so ändern, dass nur Questions mit Choices veröffentlicht werden. In diesem Fall werden viele unserer bestehenden Tests fehlschlagen - und das sagt uns genau, welche Tests geändert werden müssen, um sie auf dem neuesten Stand zu bringen - sofern Tests sich in gewisser Weise selber pflegen.

Im schlimmsten Fall können Sie beim weiteren Entwickeln feststellen, dass Sie einige Tests jetzt redundant sind. Auch das ist kein Problem; in der Testwelt ist Redundanz ein gutes Ding.

Solange Ihre Tests vernünftig strukturiert sind, werden sie nicht unüberwaltigbar. Gute allgemeine Regeln sind:

  • eine separate TestClass für jedes Modell oder jede Ansicht
  • eine separate Testmethode für jede Bedingung, die Sie testen möchten
  • Testmethodennamen, die ihre Funktion beschreiben

Weitere Tests

In diesem Tutorial werden nur einige der Grundlagen des Testens vorgestellt. Es gibt noch viel mehr, was Sie tun können, und eine Reihe sehr nützlicher Tools, mit denen Sie sehr intelligente Dinge erreichen können.

Zum Beispiel haben unsere Tests hier einige der internen Logik eines Modells und die Art, wie unsere Ansichten Informationen veröffentlichen, abgedeckt, aber Sie können ein "in-browser"-Framework wie Selenium verwenden, um zu testen, wie Ihre HTML-Seite tatsächlich in einem Browser gerendert wird. Mit diesen Tools können Sie nicht nur das Verhalten Ihres Django-Codes, sondern auch beispielsweise das von Ihrem JavaScript überprüfen. Es ist ziemlich beeindruckend, zu sehen, wie die Tests einen Browser starten und mit Ihrer Website interagieren, als ob ein Mensch sie bediene! Django enthält ~django.test.LiveServerTestCase, um die Integration mit Tools wie Selenium zu erleichtern.

Wenn Sie eine komplexe Anwendung haben, möchten Sie möglicherweise Tests automatisch mit jedem Commit für die Zwecke der kontinuierlichen Integration ausführen, so dass die Qualitätssicherung zumindest teilweise automatisiert ist.

Ein guter Weg, um ungetestete Teile Ihrer Anwendung zu entdecken, ist die Prüfung der Codeabdeckung. Dies hilft auch, fragiles oder sogar todes 代码 zu identifizieren. Wenn Sie einen Codeausschnitt nicht testen können, bedeutet das normalerweise, dass der Code umgebaut oder entfernt werden sollte. Die Abdeckung wird helfen, todes 代码 zu identifizieren. Einzelheiten finden Sie unter topics-testing-code-coverage.

Testing in Django </topics/testing/index> enthält umfassende Informationen zum Testen.

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Labor "Create Some Automated Tests" abgeschlossen. Sie können in LabEx weitere Labs absolvieren, um Ihre Fähigkeiten zu verbessern.