Ich weiß, dass dies eine umstrittene Praxis ist, aber nehmen wir an, dass dies die beste Option für mich ist. Ich frage mich, was ist die eigentliche Technik, um dies zu tun. Der Ansatz, den ich sehe, ist folgender:
1) Machen Sie eine Freundesklasse zu der Klasse, deren Methode ich testen möchte.
2) Erstellen Sie in der Friend-Klasse eine oder mehrere öffentliche Methoden, die die privaten Methoden der getesteten Klasse aufrufen.
3) Testen Sie die öffentlichen Methoden der Friend-Klasse.
Hier ist ein einfaches Beispiel, um die obigen Schritte zu veranschaulichen:
#include <iostream>
class MyClass
{
friend class MyFriend; // Step 1
private:
int plus_two(int a)
{
return a + 2;
}
};
class MyFriend
{
public:
MyFriend(MyClass *mc_ptr_1)
{
MyClass *mc_ptr = mc_ptr_1;
}
int plus_two(int a) // Step 2
{
return mc_ptr->plus_two(a);
}
private:
MyClass *mc_ptr;
};
int main()
{
MyClass mc;
MyFriend mf(&mc);
if (mf.plus_two(3) == 5) // Step 3
{
std::cout << "Passed" << std::endl;
}
else
{
std::cout << "Failed " << std::endl;
}
return 0;
}
Bearbeiten:
Ich sehe, dass sich in der folgenden Diskussion eine der Antworten über meine Codebasis wundert.
Meine Klasse verfügt über Methoden, die von anderen Methoden aufgerufen werden. Keine dieser Methoden sollte außerhalb der Klasse aufgerufen werden, daher sollten sie privat sein. Natürlich könnten sie zu einer Methode zusammengefasst werden, aber logischerweise sind sie viel besser voneinander getrennt. Diese Methoden sind kompliziert genug, um Einheitentests zu rechtfertigen, und aufgrund von Leistungsproblemen muss ich diese Methoden sehr wahrscheinlich neu faktorisieren. Daher wäre es schön, einen Test zu haben, um sicherzustellen, dass mein Re-Factoring nichts kaputt macht. Ich bin nicht der einzige, der im Team arbeitet, obwohl ich der einzige bin, der an diesem Projekt arbeitet, einschließlich der Tests.
Nach alledem ging es bei meiner Frage nicht darum, ob es eine gute Praxis ist, Komponententests für private Methoden zu schreiben, obwohl ich das Feedback schätze.
quelle
Antworten:
Eine Alternative zu Freunden, die ich häufig benutze, ist ein Muster, das ich als access_by kenne. Es ist ziemlich einfach:
Angenommen, Klasse B ist am Testen von A beteiligt. Sie können Folgendes schreiben:
Sie können dann diese Spezialisierung von access_by verwenden, um private Methoden von A aufzurufen. Im Grunde bedeutet dies, dass Sie die Freundschaft in die Header-Datei der Klasse eintragen, die die privaten Methoden von A aufrufen möchte. Sie können auch Freunde zu A hinzufügen, ohne die Quelle von A zu ändern. Idiomatisch zeigt es auch an, wer die Quelle von A liest, dass A B keinen wahren Freund im Sinne einer Erweiterung seiner Schnittstelle anzeigt. Vielmehr ist die Schnittstelle von A vollständig und B benötigt einen speziellen Zugriff auf A (Tests sind ein gutes Beispiel, ich habe dieses Muster auch bei der Implementierung von Boost-Python-Bindungen verwendet, manchmal ist eine Funktion, die in C ++ privat sein muss, praktisch in die Python-Ebene für die Implementierung).
quelle
friend access_by
, ist nicht der erste Nicht-Freund ausreichend - als verschachtelte Struktur hätte er Zugriff auf alles in A? z.B. coliru.stacked-crooked.com/a/663dd17ed2acd7a3Wenn es schwer zu testen ist, ist es schlecht geschrieben
Wenn Sie eine Klasse mit privaten Methoden haben, die komplex genug sind, um ihren eigenen Test zu rechtfertigen, tut die Klasse zu viel. Drinnen ist eine andere Klasse, die versucht, rauszukommen.
Extrahieren Sie die privaten Methoden, die Sie testen möchten, in eine neue Klasse (oder Klassen) und machen Sie sie öffentlich. Testen Sie die neuen Klassen.
Dieses Refactoring erleichtert nicht nur das Testen des Codes, sondern auch das Verständnis und die Pflege des Codes.
quelle
Sie sollten keine privaten Methoden testen. Zeitraum. Die Klassen, die Ihre Klasse verwenden, kümmern sich nur um die Methoden, die sie bereitstellen, nicht um die, die sie unter der Haube verwenden, um zu arbeiten.
Wenn Sie sich Gedanken über die Codeabdeckung machen, müssen Sie Konfigurationen finden, mit denen Sie diese private Methode über einen der öffentlichen Methodenaufrufe testen können. Wenn Sie das nicht können, wozu ist die Methode dann überhaupt gut? Es ist einfach nicht erreichbarer Code.
quelle
Hierfür gibt es einige Optionen. Bedenken Sie jedoch, dass sie (im Wesentlichen) die öffentliche Schnittstelle Ihrer Module ändern, um Ihnen Zugriff auf interne Implementierungsdetails zu gewähren (und Unit-Tests effektiv in eng gekoppelte Client-Abhängigkeiten umzuwandeln, sofern dies erforderlich ist) überhaupt keine Abhängigkeiten).
Sie können der getesteten Klasse eine Friend-Deklaration (Klasse oder Funktion) hinzufügen.
Sie können
#define private public
den Anfang Ihrer Testdateien anfügen , bevor Sie#include
den getesteten Code eingeben. Falls der getestete Code eine bereits kompilierte Bibliothek ist, können die Header möglicherweise nicht mehr mit dem bereits kompilierten Binärcode übereinstimmen (und UB verursachen).Sie können ein Makro in Ihre getestete Klasse einfügen und zu einem späteren Zeitpunkt entscheiden, was dieses Makro bedeutet (mit einer anderen Definition für das Testen von Code). Auf diese Weise können Sie Interna testen, aber Sie können auch Client-Code von Drittanbietern in Ihre Klasse hacken (indem Sie in der hinzugefügten Deklaration eine eigene Definition erstellen).
quelle
Hier ist ein fragwürdiger Vorschlag für eine fragwürdige Frage. Ich mag das Koppeln von Freunden nicht, da der dann veröffentlichte Code über den Test Bescheid wissen muss. Nirs Antwort ist eine Möglichkeit, das zu lindern, aber ich mag es immer noch nicht, die Klasse so zu ändern, dass sie dem Test entspricht.
Da ich mich nicht oft auf die Vererbung verlasse, mache ich manchmal einfach die ansonsten privaten Methoden geschützt und lasse eine Testklasse nach Bedarf erben und verfügbar machen. Die Realität ist, dass sich die öffentliche API und die Test-API von der privaten API unterscheiden können, was zu einer Art Bindung führt.
Hier ist ein praktisches Beispiel, bei dem ich auf diesen Trick zurückgreife. Ich schreibe eingebetteten Code und wir verlassen uns ziemlich oft auf Zustandsautomaten. Die externe API muss nicht unbedingt über den internen Status der Zustandsmaschine informiert sein, der Test sollte jedoch (möglicherweise) die Konformität mit dem Zustandsmaschinendiagramm im Designdokument überprüfen. Ich könnte einen "Current State" Getter als geschützt aussetzen und dann den Testzugriff darauf gewähren, so dass ich die Zustandsmaschine vollständiger testen kann. Ich finde es oft schwierig, diese Art von Klasse als Black Box zu testen.
quelle
Sie können Ihren Code mit vielen Problemumgehungen schreiben, um zu verhindern, dass Sie Freunde verwenden müssen.
Sie können Klassen schreiben und haben überhaupt keine privaten Methoden. Alles, was Sie dann tun müssen, ist Implementierungsfunktionen innerhalb der Kompilierungseinheit vorzunehmen, Ihre Klasse sie aufrufen zu lassen und alle Datenelemente weiterzuleiten, auf die sie zugreifen müssen.
Und ja, das bedeutet, dass Sie die Signaturen ändern oder neue "Implementierungs" -Methoden hinzufügen können, ohne Ihren Header in Zukunft zu ändern.
Sie müssen abwägen, ob es sich lohnt oder nicht. Und eine Menge wird wirklich davon abhängen, wer Ihren Header sehen wird.
Wenn ich eine Bibliothek eines Drittanbieters verwende, möchte ich lieber keine Freundeserklärungen für ihre Unit-Tester sehen. Ich möchte auch nicht ihre Bibliothek bauen und ihre Tests laufen lassen, wenn ich das tue. Leider tun das zu viele Open-Source-Bibliotheken von Drittanbietern, die ich erstellt habe.
Testen ist Aufgabe der Autoren der Bibliothek, nicht der Benutzer.
Es sind jedoch nicht alle Klassen für den Benutzer Ihrer Bibliothek sichtbar. Viele Klassen sind "Implementierungen" und Sie implementieren sie am besten, um sicherzustellen, dass sie ordnungsgemäß funktionieren. In diesen haben Sie möglicherweise noch private Methoden und Mitglieder, möchten aber, dass Unit-Tester sie testen. Gehen Sie also so vor, wenn dies schneller zu robustem Code führt, der für diejenigen, die dies tun müssen, einfach zu warten ist.
Wenn sich die Benutzer Ihrer Klasse alle in Ihrem eigenen Unternehmen oder Team befinden, können Sie diese Strategie auch etwas lockerer gestalten, sofern dies nach den Kodierungsstandards Ihres Unternehmens zulässig ist.
quelle