Ich schreibe Komponententests für einen Teil meines Codes und bin auf einen Fall gestoßen, in dem ich ein Objekt mit einer kleinen exponierten Schnittstelle, aber komplexen internen Strukturen habe, da jede exponierte Methode eine große Anzahl interner Funktionen durchläuft, einschließlich Abhängigkeiten auf dem Objekt Zustand. Dies macht die Methoden auf der externen Schnittstelle ziemlich schwierig zu testen.
Meine erste Frage ist, ob ich auch diese internen Funktionen testen will, weil sie einfacher sind und daher leichter Tests schreiben können? Mein Bauchgefühl sagt ja, was zur Folgefrage führt, wenn ja, wie würde ich das in C ++ machen?
Die Optionen, die ich gefunden habe, sind diese internen Funktionen von privat zu geschützt zu ändern und entweder eine Freundklasse oder eine Erbschaft zu verwenden, um auf diese internen Funktionen zuzugreifen. Ist dies die beste / einzige Methode, dies zu tun, wird ein Teil der Semantik bewahrt, die internen Methoden verborgen zu halten?
Kurze Antwort: ja.
Wie ich vor ein paar Tagen eine vorübergehende Referenz auf SO gefunden habe:
%Vor% im Unit-Test-Code, der ausgewertet wird, bevor die relevanten Header gelesen werden ...
Ebenso für geschützte.
Sehr coole Idee.
Etwas längere Antwort: Testen Sie, ob der Code nicht offensichtlich korrekt ist. Was im Wesentlichen jeden Code bedeutet, der etwas nicht-triviales tut.
Wenn ich darüber nachdenke, frage ich mich. Sie können nicht mit derselben Objektdatei verknüpfen, die Sie im Produktions-Build verwenden. Nun ist Komponententest ein bisschen eine künstliche Umgebung, also ist dies vielleicht kein Deal-Breaker. Kann jemand die Vor- und Nachteile dieses Tricks beleuchten?
Wenn Ihr Objekt hochkomplexe Operationen durchführt, die nur sehr schwer über die eingeschränkte öffentliche Schnittstelle getestet werden können, besteht eine Option darin, einen Teil dieser komplexen Logik in Dienstprogrammklassen auszufiltern, die bestimmte Aufgaben kapseln. Sie können diese Klassen dann einzeln testen. Es ist immer eine gute Idee, Ihren Code in leicht verdauliche Chunks zu organisieren.
Mein persönliches Gefühl ist, dass, wenn das Testen der öffentlichen Schnittstelle nicht ausreicht, um die privaten Methoden ausreichend zu testen, die Klasse wahrscheinlich weiter zerlegt werden muss. Meine Argumentation lautet: Private Methoden sollten nur ausreichen, um alle Anwendungsfälle der öffentlichen Schnittstelle zu unterstützen.
Aber meine Erfahrung mit Komponententests ist (leider) gering; Wenn jemand ein überzeugendes Beispiel liefern kann, wo ein großer Teil des privaten Codes nicht getrennt werden kann, bin ich sicherlich bereit, es zu überdenken!
Es gibt mehrere mögliche Ansätze. Angenommen, Ihre Klasse ist X:
Meine Meinung ist nein, generell sollten sie nicht direkt getestet werden.
Unit Tests sind White Box, aus einer höheren Perspektive des Systems, aber sie sollten Black Box aus der Perspektive der getesteten Klassenschnittstelle sein (ihre öffentlichen Methoden und ihr erwartetes Verhalten). p>
Also zum Beispiel eine String-Klasse (die keine char * Unterstützung benötigt):
Damit können Sie die Implementierung fast umgestalten, ohne die Tests später zu berühren Dies hilft Ihnen, die Kopplung zu reduzieren, indem Sie Klassenverantwortlichkeiten durchsetzen Dies erleichtert die Wartung Ihrer Tests
Die Ausnahme ist für ziemlich komplexe Hilfsmethoden, die Sie genauer überprüfen möchten.
Aber dann könnte dies ein Hinweis darauf sein, dass dieses Stück Code "offiziell" sein sollte, indem es öffentlich veröffentlicht oder in seiner eigenen Klasse mit seinen öffentlichen Methoden extrahiert wird.
Ich würde sagen, dass ich ein Code-Coverage-Tool verwenden soll, um zu überprüfen, ob diese Funktionen bereits irgendwie getestet wurden.
Wenn Ihre öffentliche API alle Tests besteht, funktionieren die privaten Funktionen theoretisch einwandfrei, solange jedes mögliche Szenario abgedeckt ist. Das ist das Hauptproblem, denke ich.
Ich weiß, dass es Werkzeuge für das Arbeiten mit C / C ++ gibt. CoverageMeter ist einer von ihnen.
Wenn Sie keine allgemeine Bibliothek erstellen, sollten Sie versuchen, das, was Sie gebaut haben, darauf zu beschränken, was Sie verwenden werden. Erweitern Sie, wie Sie es brauchen. Daher sollten Sie eine vollständige Codeabdeckung haben, und Sie sollten alles testen.
Vielleicht ist Ihr Code ein bisschen stinkend? Ist es Zeit zu refaktorisieren? Wenn Sie eine große Klasse haben, die viele Dinge intern erledigt, sollte sie vielleicht in mehrere kleinere Klassen mit Schnittstellen aufgeteilt werden, die Sie separat testen können.
Ich habe immer gedacht, dass dies dazu tendieren würde, wenn Sie testgetriebene Entwicklung verwenden würden. Es gibt zwei Möglichkeiten, sich der Entwicklung zu nähern, entweder Sie beginnen mit Ihrer öffentlichen Schnittstelle und schreiben vor jeder Hinzufügung zu den komplexen privaten Methoden einen neuen Test, oder Sie beginnen mit der Arbeit an den komplexen Dingen als öffentlich und refaktorieren den Code, um die Methoden zu privatisieren und die Tests, die Sie bereits geschrieben haben, um die neue öffentliche Schnittstelle zu verwenden. In jedem Fall sollten Sie die volle Abdeckung erhalten.
Natürlich habe ich es noch nie geschafft, eine ganze App (oder sogar Klasse) auf eine strikte TDD-Art zu schreiben, und das Refactoring der komplexen Sachen in Utility-Klassen ist der beste Weg, wenn möglich.
Ja, das solltest du. Es heißt White-Box-Test, das bedeutet, dass Sie viel über die Interna des Programms wissen müssen, um es richtig zu testen.
Ich würde öffentliche "Stubs" erstellen, die die privaten Funktionen zum Testen aufrufen. #ifdef die Stubs, so dass Sie sie kompilieren können, wenn der Test abgeschlossen ist.
Sie könnten es produktiv finden, ein Testprogramm zu schreiben. Erstellen Sie im Testprogramm eine Klasse, die die zu testende Klasse als Basis verwendet.
Fügen Sie Ihrer neuen Klasse Methoden hinzu, um die Funktionen zu testen, die an der öffentlichen Schnittstelle nicht sichtbar sind. Lassen Sie Ihr Programm einfach testen, rufen Sie diese Methoden auf, um die Funktionen zu überprüfen, die Sie betreffen.
Tags und Links unit-testing c++