Ich mache jetzt seit einem Jahr TDD, ich fühle mich ziemlich gut dabei, ich liebe meine Testsuiten und alles. Ich habe jedoch festgestellt, dass ich in letzter Zeit viele Scheinanrufüberprüfungen durchgeführt habe. Zum Beispiel hätte ich einen Dienst, dem ein Repository injiziert wird - in meinem Komponententest würde ich ein Modell des Repositorys übergeben und überprüfen, ob es innerhalb der von mir getesteten Methode aufgerufen wurde. Ich würde dann prüfen, ob die zurückgegebenen Ergebnisse korrekt sind (in einem anderen Test). Dies "fühlt" sich definitiv falsch an, da meine Unit-Tests jetzt sehr stark an die Implementierungsdetails gekoppelt sind. Ich habe gehört, dass Sie "Verhalten" testen sollten, aber in vielen Situationen, die ... emm - nicht möglich sind? Wenn Sie eine habenvoid
Methode zum Beispiel testen Sie in der Regel Nebenwirkungen. Ich meine, es ist einfach, einige einfache Code-Kata zu zeigen, in denen dies demonstriert werden kann, aber meiner Meinung nach spiegelt es die von uns geschriebenen Programme der realen Welt nicht sehr gut wider. Ist das, was ich falsch mache? Ist diese Art des Testens eine Art Anti-Muster? Ich würde mich über Ihre Meinung dazu freuen. Ich bin immer noch ein Neuling, wenn es um TDD geht.
quelle
Antworten:
Nun, Sie sollten versuchen, Ein- und Ausgänge zu testen. Sie sollten das extern sichtbare Verhalten überprüfen. Die "Versprechen" oder "Verträge", die Ihre Klasse macht.
Gleichzeitig gibt es manchmal keinen besseren Weg, eine Methode zu testen, als das zu tun, was Sie gesagt haben.
Ich denke, dass Ihr Test dadurch spröder wird. Sie sollten daher Tests vermeiden, die sich auf Implementierungsdetails stützen, wenn Sie können, aber es ist kein Alles-oder-Nichts-Geschäft. Manchmal ist es in Ordnung. Das Schlimmste, was passiert, ist, dass Sie die Implementierung ändern und den Test aktualisieren müssen.
quelle
Der Zweck eines Tests besteht darin, die möglichen produktiven Implementierungen einzuschränken. Stellen Sie sicher, dass Sie nur die Implementierung einschränken, die Sie tatsächlich benötigen. Normalerweise ist dies , was sollte Ihr Programm tun, und nicht , wie sie es tut.
Wenn Ihr Service beispielsweise dem Repository etwas hinzufügt, sollten Sie testen, ob der neue Eintrag anschließend im Repository enthalten ist und nicht, dass die Aktion zum Hinzufügen ausgelöst wird.
Damit dies funktioniert, müssen Sie in der Lage sein, die Repository-Implementierung (an anderer Stelle getestet) für den Test des Dienstes zu verwenden. Ich fand, dass die Verwendung der realen Implementierung eines Mitarbeiters im Allgemeinen ein guter Ansatz ist - weil es wirklich die beste Implementierung ist, die es gibt.
"Also, aber was ist, wenn die Verwendung der realen Implementierungen im Test teuer ist (z. B. weil sie Ressourcen erfordern, deren Einrichtung kompliziert ist)? Ich muss in diesem Fall Mocks verwenden, richtig?"
In jedem Fall möchten Sie wahrscheinlich einen Integrationstest, der prüft, ob die tatsächlichen Implementierungen zusammenarbeiten. Stellen Sie sicher, dass dieser eine Integrationstest alles ist, was zum Testen Ihres Dienstes erforderlich ist. Oder mit anderen Worten: Wenn ein Dienst viele Mitarbeiter zusammenbringt (und daher möglicherweise schwer zu testen ist), stellen Sie sicher, dass er keine Logik enthält. Wenn dies der Fall ist und Sie mehrere (Integrations-) Tests benötigen, müssen Sie die Struktur Ihres Codes ändern, z. B. indem Sie die Logik isolieren und damit testbarer machen.
Die Verwendung von Mocks in diesem Fall erleichtert das Testen einer schlecht isolierten Logik und verbirgt somit ein architektonisches Problem . Verwenden Sie also keine Mocks, um schlecht strukturierten Code zu testen, sondern korrigieren Sie stattdessen die Struktur.
quelle
Meine Gedanken zu: "Aggregatdienste".
Die Anrufüberprüfung wird dies tun, aber nicht viel Wert liefern. Sie überprüfen nur Ihre Verkabelung.
Es gibt drei nicht exklusive andere Möglichkeiten:
Erweitern Sie die Tests, die Sie für jeden einzelnen Dienst haben, um das Verhalten auf höherer Ebene zu überprüfen. Wenn Sie beispielsweise in Ihren Komponententests des Dienstes auf eine In-Memory-Datenbank zugreifen, erhöhen Sie diese, sodass Sie den Dienst anhand einer tatsächlichen Datenbank testen. Die Service-Schicht befindet sich weiter oben im Abstraktionsbaum, ebenso wie Ihr Test.
Verwenden Sie die Codegenerierung, um den Service direkt aus den aggregierten Services zu erstellen.
Verwenden Sie eine Art Reflexion oder eine dynamische Sprache, um dasselbe zu tun. In Java kann es beispielsweise möglich sein, eine groovige Schnittstelle zu verwenden, die den Anruf direkt weiterleitet.
Es gibt wahrscheinlich andere Möglichkeiten, dies zu tun, aber nur die Überprüfung der Verkabelung hat eine sehr geringe Amortisation und wird Sie hart in diese Implementierung einbinden.
quelle