Sollen nicht-öffentliche Funktionen getestet werden und wie?

8

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?

    
dlanod 12.02.2009, 03:22
quelle

11 Antworten

11

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?

    
dmckee 12.02.2009, 03:27
quelle
15

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.

    
Ates Goral 12.02.2009 03:47
quelle
5

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!

    
j_random_hacker 12.02.2009 03:46
quelle
5

Es gibt mehrere mögliche Ansätze. Angenommen, Ihre Klasse ist X:

  1. Verwenden Sie nur die öffentliche Schnittstelle von X. Sie werden umfangreiche Setup-Probleme haben und benötigen möglicherweise ein Coverage-Tool, um sicherzustellen, dass Ihr Code abgedeckt ist, aber keine speziellen Tricks beteiligt sind.
  2. Verwenden Sie den "# private private public" - oder einen ähnlichen Trick, um eine Verknüpfung mit einer Version von X.o herzustellen, die für alle verfügbar ist.
  3. Fügen Sie eine öffentliche Methode "static X :: unitTest ()" hinzu. Dies bedeutet, dass der Code an Ihr Testframework gesendet wird. (Eine Firma, mit der ich zusammenarbeitete, nutzte dies für die Ferndiagnose der Software.)
  4. Füge "class TestX" als Freund von X hinzu. TestX wird nicht in deiner Produktionsdll / exe ausgeliefert. Es ist nur in Ihrem Testprogramm definiert, aber es hat Zugriff auf die Interna von X.
  5. Andere ...
Andrew Stein 12.02.2009 04:01
quelle
3

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):

  • Sie sollten sicherstellen, dass die Methode length () korrekt funktioniert.
  • Sie sollten nicht überprüfen müssen, ob das Zeichen '\ 0' am Ende des internen Puffers steht. Dies ist ein Implementierungsdetail.

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.

    
jturcotte 12.02.2009 05:14
quelle
2

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.

    
Edwin Jarvis 12.02.2009 03:42
quelle
2

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.

    
Greg Domjan 12.02.2009 03:44
quelle
2

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.

    
Patrick 12.02.2009 10:15
quelle
0

Sie könnten immer einen Compile-Schalter um den privaten verwenden: wie

#if definiert (UNIT_TEST)

Oder stellen Sie mithilfe von Code-Coverage-Tools sicher, dass Ihr Unit-Test Ihrer öffentlichen Funktionen die privaten Funktionen vollständig ausübt.

    
apekarske 12.02.2009 03:27
quelle
0

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.

    
Gregor Brandt 12.02.2009 03:28
quelle
0

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.

    
EvilTeach 12.02.2009 17:41
quelle

Tags und Links