Ich kämpfe wirklich darum, effektive Komponententests für ein großes Django-Projekt zu schreiben. Ich habe eine einigermaßen gute Testabdeckung, habe jedoch festgestellt, dass es sich bei den von mir erstellten Tests definitiv um Integrations- / Akzeptanztests handelt, überhaupt nicht um Komponententests, und ich habe kritische Teile meiner Anwendung, die nicht effektiv getestet werden. Ich möchte dieses Problem so schnell wie möglich beheben.
Hier ist mein Problem. Mein Schema ist zutiefst relational und stark zeitorientiert, was meinem Modellobjekt eine hohe interne Kopplung und viel Zustand verleiht. Viele meiner Modellmethoden fragen basierend auf Zeitintervallen ab, und ich habe viel mit auto_now_add
Zeitstempeln zu tun. Nehmen Sie also eine Methode, die zum Beispiel so aussieht:
def summary(self, startTime=None, endTime=None):
# ... logic to assign a proper start and end time
# if none was provided, probably using datetime.now()
objects = self.related_model_set.manager_method.filter(...)
return sum(object.key_method(startTime, endTime) for object in objects)
Wie geht man vor, um so etwas zu testen?
Hier bin ich soweit. Es fällt mir ein, dass das Unit-Test-Ziel ein falsches Verhalten by key_method
in Bezug auf seine Argumente zeigen sollte. summary
Wird richtig gefiltert / aggregiert, um ein korrektes Ergebnis zu erzielen?
Datetime.now () zu verspotten ist einfach genug, aber wie kann ich den Rest des Verhaltens verspotten?
- Ich könnte Fixtures verwenden, aber ich habe Vor- und Nachteile der Verwendung von Fixtures zum Erstellen meiner Daten gehört (schlechte Wartbarkeit ist für mich ein Nachteil).
- Ich könnte meine Daten auch über den ORM einrichten, aber das kann einschränkend sein, da ich dann auch verwandte Objekte erstellen muss. Und mit dem ORM können Sie nicht
auto_now_add
manuell mit Feldern herumspielen. - Das Verspotten des ORM ist eine weitere Option, aber es ist nicht nur schwierig, tief verschachtelte ORM-Methoden zu verspotten, sondern die Logik im ORM-Code wird aus dem Test herausverspottet, und das Verspotten scheint den Test wirklich von den Interna und Abhängigkeiten des zu abhängig zu machen zu prüfende Funktion.
Die am schwierigsten zu knackenden Nüsse scheinen Funktionen wie diese zu sein, die auf einigen Schichten von Modellen und Funktionen auf niedrigerer Ebene basieren und sehr zeitabhängig sind, auch wenn diese Funktionen möglicherweise nicht sehr kompliziert sind. Mein generelles Problem ist, dass meine Tests, egal wie ich sie aufschlitze, viel komplexer aussehen als die Funktionen, die sie testen.
quelle
Antworten:
Ich werde weitermachen und eine Antwort für das eintragen, was ich mir bisher ausgedacht habe.
Meine Hypothese ist, dass für eine Funktion mit tiefer Kopplung und Zustand die Realität ist, dass es einfach eine Menge Zeilen braucht, um den äußeren Kontext zu kontrollieren.
So sieht mein Testfall in etwa aus, wobei ich mich auf die Standard-Mock-Bibliothek stütze:
datetime
und kehre dieauto_now_add
Zeiten um, um sie an eine feste Zeitachse meines Designs anzupassen. Ich dachte, dass der ORM dies nicht zulässt, aber es funktioniert gut.from datetime import datetime
damit ichdatetime.now()
genau diese Funktion patchen kann (wenn ich die gesamtedatetime
Klasse verspotte , ist der ORM fit).object.key_method()
, mit einfacher, aber klar definierter Funktionalität, die von den Argumenten abhängt. Ich möchte, dass es von den Argumenten abhängt, da ich sonst möglicherweise nicht weiß, ob die Logik der zu testenden Funktion funktioniert. In meinem Fall wird einfach die Anzahl der Sekunden zwischenstartTime
und zurückgegebenendTime
. Ich baue es ein, indem ich es in ein Lambda einwickle und direktobject.key_method()
mit demnew_callable
kwarg von aufbauepatch
.summary
mit unterschiedlichen Argumenten durch, um die Gleichheit mit den erwarteten handkalkulierten Ergebnissen zu überprüfen, die das vorgegebene Verhalten des Mocks berücksichtigenkey_method
Dies ist natürlich wesentlich länger und komplizierter als die eigentliche Funktion. Es hängt von der DB ab und fühlt sich nicht wirklich wie ein Komponententest an. Es ist aber auch ziemlich von den Interna der Funktion entkoppelt - nur von ihrer Signatur und ihren Abhängigkeiten. Ich denke, es könnte immer noch ein Unit-Test sein.
In meiner App ist die Funktion sehr zentral und muss überarbeitet werden, um die Leistung zu optimieren. Ich denke, der Aufwand lohnt sich trotz der Komplexität. Aber ich bin immer noch offen für bessere Ideen, wie ich das angehen kann. Alles Teil meiner langen Reise in Richtung eines testgetriebenen Entwicklungsstils ...
quelle