Komponententests - Festlegen von privaten Mitgliedern, um den gewünschten Objektstatus zu erhalten

9

Ich mache meine ersten Schritte mit Komponententests und habe ein Problem mit der Kapselung. Meine Klasse hat einige private Member-Variablen, die für den Client nicht sichtbar sein sollten, aber damit ich das Objekt in einen Zustand versetzen kann, unter dem ich es testen möchte, muss ich diese privaten Variablen setzen.

Sagen Sie, ich habe einen Code wie diesen:

%Vor%

Nun möchte ich Foo::action() testen, aber ich muss Foo::state einstellen können, um die Funktion in verschiedenen Szenarien überprüfen zu können. Eine Lösung ist das böse " define private public " im Testcode. Aber gibt es etwas Eleganteres? Ich möchte betonen, dass Foo::state eine Variable ist, auf die der Client nicht zugreifen sollte, deshalb möchte ich keinen öffentlichen Setter deklarieren.

Bearbeiten:

Ich denke jetzt, dass die Erweiterung der Klasse, die ich in Testcode testen möchte, und das Einfügen von Setter in diese abgeleitete Klasse funktionieren würde, vorausgesetzt, ich habe private Variablen in protected geändert. Aber das ist eine "one generation only" -Lösung und fühlt sich immer noch eher wie ein Hack an als ein richtiger Ansatz.

Bearbeiten 2:

Nachdem ich die Antworten und Kommentare gelesen hatte (danke an Lieven und insbesondere ap) glaube ich, dass die eigentliche Klasse, die ich jetzt testen möchte (nicht das einfache Beispiel, das ich zur Verfügung gestellt habe), einfach zu viel und die Antwort auf meine Problem ist das Verschieben einiger seiner Logik in eine andere Klasse, die von dem großen Kerl verwendet wird.

    
Puchatek 20.12.2012, 11:58
quelle

3 Antworten

3

Es gibt nur zwei Möglichkeiten (refactoring asside)

  1. Verwenden Sie die öffentliche Schnittstelle , um den Status festzulegen.
  2. Der Status ist redundant , wenn Sie ihn nicht über die öffentliche Schnittstelle festlegen können.

Option 2 ist selbsterklärend und höchstwahrscheinlich nicht auf Ihren Fall anwendbar, so dass Sie den Status über die öffentliche Schnittstelle Ihrer Klasse festlegen müssen.

Wie Sie bereits erwähnt haben, ist dies möglich, aber es erfordert viel Code, um in den richtigen Zustand zu gelangen. Das kann ein Hinweis darauf sein, dass Ihre Klasse gerade zu viel tut, und es ist an der Zeit, Teile Ihrer Klasse in kleinere, testbare Klassen umzuformen.

Von Testen Sie keine privaten Methoden

  

Wenn Sie das Bedürfnis haben, eine private Methode zu testen, dann tun Sie es   etwas anderes stimmt nicht. Es gibt sozusagen ein "Upstream" -Problem. Sie   sind aufgrund eines anderen Fehlers in diesem Problem angekommen   dieser Prozess. Versuche zu isolieren, was genau ist, und entferne das   anstatt Ihre Tests zu beugen, um eine schmerzhafte Straße von zu gehen   Sprödigkeit beim Testen.

und Unit testet private Mitglieder

  

Ich würde empfehlen, keine privaten Methoden zu testen. Da sind sie   privat, sie können in jedem denkbaren (oder unvorstellbaren?) geändert werden   Art und Weise zwischen jedem Release. Wenn eine bestimmte private Methode so kritisch ist   zu der Operation der Klasse, die Sie fühlen, dass es Testfälle verdient,   Dann ist es wahrscheinlich an der Zeit, das in ein geschütztes oder umgestaltetes zu verwandeln   öffentliche Methode

Ein häufiges Zitat dazu ist

  

Sie sollten niemals Ihre Privatsphäre berühren

    
Lieven Keersmaekers 20.12.2012 12:04
quelle
1

Wenn Ihre Testklasse MyTestClass heißt, fügen Sie MyTestClass als Freund in der Klasse Foo hinzu, um auf ihre privaten Membervariablen zugreifen zu können.

%Vor%     
xavatage 20.12.2012 12:44
quelle
1

Sie sollten einen% code_% ly (oder " public ly") Mechanismus haben, um den Wert der privaten Variable protected zu ändern. Zur Einfachheit sagen wir, es ist eine Methode state . Verwenden Sie diese im Komponententest, um den Status zu ändern, und testen Sie die Foo::setState(int inState) -Methode. Dies stellt sicher, dass zukünftige Implementierungsänderungen den Komponententest nicht beeinflussen (es sei denn, die "API" Foo::action() ändert sich - in diesem Fall müssen Sie natürlich den Komponententest ändern).

Wenn Sie nicht einen solchen Mechanismus zum Ändern von Foo::setState() haben, bedeutet dies, dass der Endbenutzer oder der aufrufende Code sie auch nicht ändern kann und Sie ihn daher nicht testen müssen (und vielleicht macht das state überflüssig, aber das weiß ich nicht.)

Wenn sich die private Variable "indirekt" durch anderen Code ändert, müssen Sie denselben Code im Komponententest ausführen. Sie können jederzeit jede extern sichtbare Methode auf Eingänge zurückführen, die dem Code extern zugeführt wurden. Grundsätzlich ist der Punkt, dass Sie im Komponententest dieselben Eingaben wie im "echten" Szenario dem Code zuführen müssen und dann testen müssen, ob er so reagiert wie er sollte.

Wenn der "Pfad" von der Eingabe zum zu testenden Code too lang ist, muss der Code / Test möglicherweise in kleinere Module oder Zwischenpunkte zerlegt werden. Um dies näher auszuführen, betrachten Sie den Code-Fluss als:

%Vor%

Im obigen Fall ist alles, was Sie tun können,

  

Gegeben state , überprüfe ob Input korrekt ist.

Stattdessen wäre es gut, das intermediate Output , A , B zumindest für die Komponententests verfügbar zu machen, so dass Sie jetzt Ihren Test in:

brechen können
  

Gegeben C , überprüfe ob Input korrekt ist.

     

Gegeben A , überprüfe ob A korrekt ist.

     

Gegeben B , überprüfe ob B korrekt ist.

     

Gegeben C , überprüfe ob C korrekt ist.

Wie Sie sich vorstellen können, wird es bei einem fehlgeschlagenen Test einfacher, herauszufinden, was fehlgeschlagen ist, und es somit zu beheben.

    
Masked Man 20.12.2012 12:46
quelle

Tags und Links