Wir haben versucht, Unit-Tests in unser aktuelles Projekt einzuführen, aber es scheint nicht zu funktionieren. Der zusätzliche Code scheint zu einem Wartungsproblem geworden zu sein, da wir bei Änderungen unseres internen Frameworks alle Unit-Tests beheben müssen, die daran hängen.
Wir haben eine abstrakte Basisklasse für Unit-Tests unserer Controller, die als Vorlage für die abstrakten Methodenimplementierungen der untergeordneten Klassen fungiert, dh Framework-Aufrufe Initialize, sodass alle unsere Controller-Klassen ihre eigene Initialize-Methode haben.
Früher war ich ein Verfechter von Unit-Tests, aber es scheint nicht an unserem aktuellen Projekt zu funktionieren.
Kann jemand helfen, das Problem zu identifizieren und wie wir Unit-Tests für uns und nicht gegen uns durchführen können?
Antworten:
Tipps:
Vermeiden Sie das Schreiben von Verfahrenscode
Tests können ein Bär sein, wenn sie gegen prozeduralen Code geschrieben werden, der stark vom globalen Zustand abhängt oder tief im Körper einer hässlichen Methode liegt. Wenn Sie Code in einer OO-Sprache schreiben, verwenden Sie OO-Konstrukte effektiv, um dies zu reduzieren.
Finden Sie, was sich ändert, kapseln Sie es und trennen Sie es von dem, was gleich bleibt.
Es gibt Drosselstellen im Code, die sich viel häufiger ändern als andere Teile. Wenn Sie dies in Ihrer Codebasis tun, werden Ihre Tests gesünder.
Je größer der Kontext eines Tests ist, desto schwieriger wird es, ihn aufrechtzuerhalten.
Tun Sie alles, um Tests und den umgebenden Kontext, in dem sie ausgeführt werden, zu verkleinern.
Entfernen Sie Duplikate aus Tests, stellen Sie jedoch sicher, dass sie den Fokus behalten.
Sie werden wahrscheinlich nicht in der Lage sein, alle Duplikate zu entfernen, aber dennoch versuchen, sie dort zu entfernen, wo sie Schmerzen verursachen. Stellen Sie sicher, dass Sie nicht so viele Duplikate entfernen, dass jemand nicht hereinkommen und auf einen Blick erkennen kann, was der Test bewirkt. ( Eine alternative Erklärung desselben Konzepts finden Sie in Paul Wheatons Artikel "Evil Unit Tests" .)
Testen Sie auf der richtigen Ebene, was Sie überprüfen möchten.
Denken Sie an die Komplexität eines Selenium-Tests zum Aufzeichnen und Wiedergeben und darüber, was sich unter Ihnen im Vergleich zum Testen einer einzelnen Methode ändern könnte.
Wissen, wann State Based vs Interaction Based Testing verwendet werden muss
Echte Unit-Tests erfordern echte Isolation. Unit-Tests treffen keine Datenbank und öffnen keine Sockets. Hören Sie auf, sich über diese Interaktionen lustig zu machen. Stellen Sie sicher, dass Sie korrekt mit Ihren Mitarbeitern sprechen und nicht, dass das richtige Ergebnis dieses Methodenaufrufs "42" war.
Demonstrieren Sie den Testfahrcode
Es steht zur Debatte, ob ein bestimmtes Team den gesamten Code testen oder "Tests zuerst" für jede Codezeile schreiben wird. Aber sollten sie zuerst mindestens einige Tests schreiben? Absolut. Es gibt Szenarien, in denen Test-First zweifellos der beste Weg ist, um ein Problem anzugehen.
Ressourcen:
quelle
Testen Sie ausreichend kleine Codeeinheiten? Sie sollten nicht zu viele Änderungen sehen, es sei denn, Sie ändern grundlegend alles in Ihrem Kerncode.
Sobald die Dinge stabil sind, werden Sie die Komponententests mehr zu schätzen wissen, aber selbst jetzt zeigen Ihre Tests, inwieweit Änderungen an Ihrem Framework durchgesetzt werden.
Es lohnt sich, bleiben Sie dabei, so gut Sie können.
quelle
Ohne weitere Informationen ist es schwierig, einen guten Eindruck davon zu bekommen, warum Sie unter diesen Problemen leiden. Manchmal ist es unvermeidlich, dass das Ändern von Schnittstellen usw. viele Dinge kaputt macht, manchmal liegt es an Designproblemen.
Es ist eine gute Idee, die aufgetretenen Fehler zu kategorisieren. Welche Probleme haben Sie? Ist es beispielsweise eine Testwartung (wie beim Kompilieren nach dem Refactoring!) Aufgrund von API-Änderungen oder liegt es am Verhalten der API-Änderung? Wenn Sie ein Muster sehen, können Sie versuchen, das Design des Produktionscodes zu ändern oder die Tests besser vor Änderungen zu schützen.
Wenn das Ändern einer Handvoll Dinge an vielen Stellen zu einer unermesslichen Verwüstung Ihrer Testsuite führt, können Sie einige Dinge tun (die meisten davon sind nur allgemeine Tipps zum Testen von Einheiten):
Entwickeln Sie kleine Codeeinheiten und testen Sie kleine Codeeinheiten. Extrahieren Sie Schnittstellen oder Basisklassen, wenn dies sinnvoll ist, damit Codeeinheiten 'Nähte' enthalten. Je mehr Abhängigkeiten Sie einbeziehen müssen (oder schlimmer noch, Sie müssen innerhalb der Klasse mit 'new' instanziieren), desto anfälliger ist es, Ihren Code zu ändern. Wenn jede Codeeinheit eine Handvoll Abhängigkeiten aufweist (manchmal ein paar oder gar keine), ist sie besser vor Änderungen geschützt.
Stellen Sie immer nur fest, was der Test benötigt. Setzen Sie sich nicht für einen Zwischen-, Neben- oder Nicht-Zustandszustand ein. Design by Contract und Test by Contract (z. B. wenn Sie eine Stack-Pop-Methode testen, testen Sie die count-Eigenschaft nach dem Push nicht - dies sollte in einem separaten Test erfolgen).
Ich sehe dieses Problem ziemlich oft, besonders wenn jeder Test eine Variante ist. Wenn sich einer dieser zufälligen Zustände ändert, wird alles unterbrochen, was darauf behauptet wird (unabhängig davon, ob die Zusicherungen benötigt werden oder nicht).
Verwenden Sie wie bei normalem Code Fabriken und Builder für Ihre Komponententests. Ich habe erfahren, dass bei etwa 40 Tests ein Konstruktoraufruf erforderlich war, der nach einer API-Änderung aktualisiert wurde ...
Verwenden Sie genauso wichtig zuerst die Vordertür. Ihre Tests sollten immer den normalen Zustand verwenden, wenn er verfügbar ist. Verwendete interaktionsbasierte Tests nur, wenn dies erforderlich ist (dh kein Status, anhand dessen überprüft werden kann).
Das Wesentliche dabei ist jedenfalls, dass ich versuchen würde herauszufinden, warum / wo die Tests abbrechen und von dort aus fortfahren. Geben Sie Ihr Bestes, um sich vor Veränderungen zu schützen.
quelle
Einer der Vorteile von Unit-Tests besteht darin, dass Sie bei solchen Änderungen nachweisen können, dass Sie Ihren Code nicht beschädigen. Sie müssen Ihre Tests zwar mit Ihrem Framework synchronisieren, aber diese eher banale Arbeit ist viel einfacher, als herauszufinden, was beim Refactoring kaputt gegangen ist.
quelle
Ich würde darauf bestehen, dass Sie sich an die TDD halten.Versuchen Sie, Ihr Unit-Testing-Framework zu überprüfen. Führen Sie mit Ihrem Team eine RCA (Root Cause Analysis) durch und identifizieren Sie den Bereich.
Korrigieren Sie den Unit-Test-Code auf Suite-Ebene und ändern Sie Ihren Code nicht häufig, insbesondere nicht die Funktionsnamen oder andere Module.
Würden Sie sich freuen, wenn Sie Ihre Fallstudie gut teilen können, dann können wir mehr über den Problembereich herausfinden?
quelle
Gute Frage!
Das Entwerfen guter Komponententests ist schwierig wie das Entwerfen der Software selbst. Dies wird von Entwicklern selten anerkannt, so dass das Ergebnis häufig hastig geschriebene Komponententests sind, die gewartet werden müssen, wenn sich das zu testende System ändert. Ein Teil der Lösung Ihres Problems könnte darin bestehen, mehr Zeit für die Verbesserung des Designs Ihrer Komponententests aufzuwenden.
Ich kann ein großartiges Buch empfehlen, das seine Abrechnung verdient The Design Patterns of Unit-Testing
HTH
quelle
Wenn das Problem darin besteht, dass Ihre Tests mit dem tatsächlichen Code nicht mehr aktuell sind, können Sie einen oder beide der folgenden Schritte ausführen:
quelle
Nun, wenn sich die Logik im Code geändert hat und Sie Tests für diese Codeteile geschrieben haben, würde ich annehmen, dass die Tests geändert werden müssten, um die neue Logik zu überprüfen. Unit-Tests sollen ziemlich einfacher Code sein, der die Logik Ihres Codes testet.
quelle
Ihre Unit-Tests machen das, was sie machen sollen. Bringen Sie Verhaltensstörungen aufgrund von Änderungen im Framework, im Sofortcode oder in anderen externen Quellen an die Oberfläche. Auf diese Weise können Sie feststellen, ob sich das Verhalten geändert hat und die Komponententests entsprechend geändert werden müssen oder ob ein Fehler aufgetreten ist, der dazu führt, dass der Komponententest fehlschlägt und korrigiert werden muss.
Gib nicht auf, während es jetzt frustrierend ist, wird der Nutzen realisiert.
quelle
Ich bin mir nicht sicher, welche spezifischen Probleme es schwierig machen, Tests für Ihren Code zu verwalten, aber ich kann einige meiner eigenen Erfahrungen teilen, als ich ähnliche Probleme mit meinen Tests hatte. Letztendlich habe ich erfahren, dass die mangelnde Testbarkeit hauptsächlich auf einige Designprobleme mit der getesteten Klasse zurückzuführen ist:
Aus diesem Grund stellte ich fest, dass meine Tests normalerweise unterbrochen wurden - nicht aufgrund einer Änderung in der zu testenden Klasse -, sondern aufgrund von Änderungen in anderen Klassen, die von der zu testenden Klasse aufgerufen wurden. Im Allgemeinen macht das Refactoring von Klassen, um nach ihren Datenabhängigkeiten zu fragen, und das Testen mit Scheinobjekten (EasyMock et al. Für Java) das Testen viel fokussierter und wartbarer. Ich habe einige Websites zu diesem Thema wirklich genossen:
quelle
Warum sollten Sie Ihre Komponententests jedes Mal ändern müssen, wenn Sie Änderungen an Ihrem Framework vornehmen? Sollte das nicht umgekehrt sein?
Wenn Sie TDD verwenden, sollten Sie zunächst entscheiden, dass Ihre Tests das falsche Verhalten testen, und stattdessen überprüfen, ob das gewünschte Verhalten vorliegt. Nachdem Sie Ihre Tests behoben haben, schlagen Ihre Tests fehl und Sie müssen die Fehler in Ihrem Framework beseitigen, bis Ihre Tests erneut erfolgreich sind.
quelle
Alles ist natürlich mit dem Preis verbunden. In diesem frühen Entwicklungsstadium ist es normal, dass viele Komponententests geändert werden müssen.
Möglicherweise möchten Sie einige Teile Ihres Codes überprüfen, um mehr Kapselung zu erzielen, weniger Abhängigkeiten zu erstellen usw.
Wenn Sie sich dem Produktionsdatum nähern, werden Sie froh sein, dass Sie diese Tests haben, vertrauen Sie mir :)
quelle
Sind Ihre Unit-Tests nicht zu Black-Box-orientiert? Ich meine ... lassen Sie mich ein Beispiel nehmen: Angenommen, Sie testen eine Art Container in einer Einheit, verwenden Sie die Methode get () des Containers, um zu überprüfen, ob ein neues Element tatsächlich gespeichert wurde, oder schaffen Sie es, ein Handle zu erhalten den tatsächlichen Speicher, um den Artikel direkt dort abzurufen, wo er gespeichert ist? Letzteres führt zu spröden Tests: Wenn Sie die Implementierung ändern, brechen Sie die Tests.
Sie sollten anhand der Schnittstellen testen, nicht anhand der internen Implementierung.
Und wenn Sie das Framework ändern, sollten Sie zuerst versuchen, die Tests und dann das Framework zu ändern.
quelle
Ich würde vorschlagen, in ein Testautomatisierungstool zu investieren. Wenn Sie eine kontinuierliche Integration verwenden, können Sie diese zusammen ausführen. Es gibt Tools, die Ihre Codebasis scannen und Tests für Sie generieren. Dann werden sie ausgeführt. Nachteil dieses Ansatzes ist, dass es zu allgemein ist. Denn in vielen Fällen besteht der Zweck des Unit-Tests darin, das System zu beschädigen. Ich habe zahlreiche Tests geschrieben und ja, ich muss sie ändern, wenn sich die Codebasis ändert.
Es gibt eine feine Linie mit dem Automatisierungstool, mit dem Sie definitiv eine bessere Codeabdeckung haben würden.
Mit einem gut entwickelten Entwickler-basierten Test testen Sie jedoch auch die Systemintegrität.
Hoffe das hilft.
quelle
Wenn Ihr Code wirklich schwer zu testen ist und der Testcode kaputt geht oder viel Aufwand erfordert, um synchron zu bleiben, haben Sie ein größeres Problem.
Verwenden Sie das Refactoring der Extraktionsmethode, um kleine Codeblöcke herauszuziehen, die nur eines und nur eines tun. ohne Abhängigkeiten und schreiben Sie Ihre Tests auf diese kleinen Methoden.
quelle
Die Alternative besteht darin, dass Sie die Änderungen nicht testen, wenn sich Ihr Framework ändert. Oder Sie testen das Framework überhaupt nicht. Ist es das was du willst?
Sie können versuchen, Ihr Framework so umzugestalten, dass es aus kleineren Teilen besteht, die unabhängig getestet werden können. Wenn sich dann Ihr Framework ändert, hoffen Sie, dass sich entweder (a) weniger Teile ändern oder (b) die Änderungen hauptsächlich in der Art und Weise liegen, wie die Teile zusammengesetzt sind. In beiden Fällen können Sie Code und Tests besser wiederverwenden. Aber es geht um echte intellektuelle Anstrengungen. Erwarten Sie nicht, dass es einfach ist.
quelle
Ich fand heraus, dass die Unit-Tests, wenn Sie nicht die IoC / DI-Methodik verwenden, die das Schreiben sehr kleiner Klassen fördert und das Prinzip der Einzelverantwortung religiös befolgt, letztendlich die Interaktion mehrerer Klassen testen, was sie sehr komplex und daher fragil macht.
Mein Punkt ist, dass viele der neuartigen Softwareentwicklungstechniken nur funktionieren, wenn sie zusammen verwendet werden. Insbesondere MVC, ORM, IoC, Unit-Testing und Mocking. DDD (im modernen primitiven Sinne) und TDD / BDD sind unabhängiger, sodass Sie sie verwenden können oder nicht.
quelle
Irgendwann werden beim Entwerfen der TDD-Tests Fragen zum Design der Anwendung selbst gestellt. Überprüfen Sie, ob Ihre Klassen gut gestaltet wurden, Ihre Methoden immer nur eine Aufgabe ausführen ... Bei gutem Design sollte es einfach sein, Code zu schreiben, um einfache Methoden und Klassen zu testen.
quelle
Ich habe selbst über dieses Thema nachgedacht. Ich bin sehr begeistert vom Wert von Unit-Tests, aber nicht von strengen TDD. Es scheint mir, dass Sie bis zu einem gewissen Punkt explorative Programmierung durchführen, bei der sich die Art und Weise ändern muss, wie Sie Dinge in Klassen / Schnittstellen aufteilen. Wenn Sie viel Zeit in Komponententests für die alte Klassenstruktur investiert haben, ist dies eine erhöhte Trägheit gegenüber Refactoring, schmerzhaftes Verwerfen dieses zusätzlichen Codes usw.
quelle