11 Stimmen

Verwendung einer Freundesklasse vs. Hinzufügen von Accessoren für Unit-Tests in C++?

Ist es besser, Funktionen hinzuzufügen, die den internen Zustand eines Objekts für Unit-Tests zurückgeben, anstatt die Testklasse zu einem Freund zu machen? - vor allem, wenn es für die Funktionen keine Verwendung gibt, außer für den Fall von Unit-Tests.

0 Stimmen

12voto

Drew Hoskins Punkte 4133

Unit-Tests sollten zu 95 % der Zeit nur die öffentlich zugängliche Oberfläche einer Klasse testen. Wenn man etwas im Verborgenen testet, testet man Implementierungsdetails, was von Natur aus anfällig ist, weil man die Implementierung leicht ändern können sollte und die Tests trotzdem funktionieren müssen. Das ist nicht nur anfällig, sondern man könnte auch in Versuchung geraten, Dinge zu testen, die in den geplanten Nutzungsszenarien gar nicht möglich sind, was Zeitverschwendung ist.

Wenn der Sinn der Accessors, die Sie hinzufügen wollen, nur darin besteht, zu testen, ob die Funktion den gewünschten Effekt hatte, könnte Ihr Klassendesign einen anderen Grundsatz verletzen, nämlich dass eine zustandsmaschinenähnliche Klasse immer deutlich machen sollte, in welchem Zustand sie sich befindet, wenn das Auswirkungen darauf hat, was passiert, wenn Menschen mit der Klasse interagieren. In diesem Fall wäre es richtig, diese Nur-Lese-Zugriffsfunktionen bereitzustellen. Wenn es keinen Einfluss auf das Verhalten der Klasse hat, verweise ich auf meinen vorherigen Abschnitt über Implementierungsdetails.

Und wie Sie richtig sagten, ist es auch aus eigenen Gründen unerwünscht, die öffentliche Oberfläche einer Klasse mit ungenutztem Material zu überladen.

Wenn ich hatte Wenn ich in Ihrem Fall zwischen Accessors und Friending wählen müsste, würde ich mich für Friending entscheiden, einfach weil Sie besitzen Ihren Test und können ihn im Handumdrehen ändern. Der Code des Clowns, der einen Weg findet, Ihre zusätzlichen Accessoren zu verwenden, gehört Ihnen möglicherweise nicht, und dann sitzen Sie fest.

10voto

Darryl Punkte 5512

Ich widerspreche der üblichen Antwort und empfehle stattdessen die Verwendung einer Freundesklasse.

Ein Teil des Zustands, den Sie testen, ist wahrscheinlich spezifisch für die Implementierung Ihrer Klasse; Sie testen Details, die anderen Code normalerweise nicht kennen oder interessieren und auf die Sie sich nicht verlassen sollten. Öffentliche Zugriffsfunktionen machen diese Implementierungsdetails zu einem Teil der Schnittstelle der Klasse. Wenn der interne Zustand, den Sie testen, nicht Teil der vorgesehenen Schnittstelle ist, sollte er nicht über öffentliche Funktionen sichtbar sein. Vom puristischen Standpunkt aus betrachtet, stecken Sie zwischen zwei falschen Antworten fest, da Freundesklassen technisch gesehen auch Teil der öffentlichen Schnittstelle sind. Meiner Meinung nach stellt sich die Frage, welche Option weniger wahrscheinlich zu schlechten Programmierentscheidungen führen wird. Wenn man sich für eine Reihe von implementierungsabhängigen öffentlichen Zugriffsfunktionen entscheidet, fördert man unbeabsichtigt ein implementierungsabhängiges konzeptionelles Modell der Klasse, was zu einer implementierungsabhängigen Verwendung der Klasse führt. Eine einzelne Freundesklasse, die angemessen benannt und dokumentiert ist, wird mit geringerer Wahrscheinlichkeit missbraucht werden.

Während ich im Allgemeinen der Empfehlung, Accessor-Funktionen dem direkten Zugriff auf Member-Variablen vorzuziehen, sehr zustimme, bin ich nicht der Meinung, dass diese Best Practice für Unit-Tests von implementierungsabhängigen internen Zuständen gilt. Ein vernünftiger Mittelweg ist die Verwendung von privat Accessor-Funktionen für die Teile des Zustands, die für Ihren Unit-Test von Bedeutung sind, und seien Sie diszipliniert genug, um die Accessor-Funktionen in Ihren Unit-Tests zu verwenden. Nur meine Meinung.

3voto

cchampion Punkte 7147

Die Verwendung von Freundesklassen für Unit-Tests ist völlig legitim und ermöglicht es Ihnen, die Kapselung beizubehalten. Sie sollten die öffentliche Schnittstelle Ihrer Klasse nicht verändern, nur um die Klasse besser testbar zu machen. Stellen Sie sich das einmal so vor. Was wäre, wenn Sie eine FTP-Bibliothek eines Drittanbieters gekauft haben und versuchen, sie zu verwenden, und ihre öffentliche Schnittstelle mit einer Reihe von Methoden überladen ist, von denen Sie nicht einmal etwas wissen müssen, nur weil Sie Unit-Tests durchführen! Selbst das Ändern einer geschützten Schnittstelle, um Unit-Tests zu kompensieren, ist SCHLECHT. Wenn ich von einer Klasse erbe, möchte ich mir keine Gedanken darüber machen müssen, welche Methoden für mich nützlich sind und welche nur wegen der Unit-Tests vorhanden sind!!! Die Verwendung von Freundesklassen für Unit-Tests hilft Ihnen, eine einfache, leichter zu verwendende Klassenschnittstelle beizubehalten; sie hilft, Kapselung und Abstraktion zu bewahren!!!

Ich habe das Argument gehört, dass die Verwendung von Freundesklassen für Unit-Tests schlecht ist, weil die zu testende Klasse nicht "eng mit ihrer Testklasse gekoppelt" sein sollte und nichts über ihre Testklasse "wissen" sollte. Ich glaube das nicht. Es handelt sich um eine einzige Zeile, die am Anfang der Klasse hinzugefügt wird:

friend class MyClassTest;

und jetzt können Sie Ihre Klasse testen, wie Sie wollen!

Ich stimme zu, dass man eine Freundesklasse nur dann verwenden sollte, wenn es notwendig ist. Wenn Sie testen können, was getestet werden muss, ohne es zu einem Freund zu machen, sollten Sie das auf jeden Fall tun. Aber wenn das Leben schwierig wird und die Verwendung einer Freundesklasse das Leben wieder einfach macht, verwenden Sie sie!

1voto

James Thompson Punkte 44724

Ich empfehle die Verwendung von Accessors, anstatt den Zugriff über öffentliche Mitglieder oder Freundesklassen zu ermöglichen.

Ich glaube nicht, dass die Nutzung der Freundschaftsklasse Ihnen wirklich Vorteile bringt, und sie hat das Potenzial, Ihr Leben auf dem Weg viel schlechter zu machen. Wenn Ihr Code für eine lange Zeit bestehen bleibt, ist die Wahrscheinlichkeit groß, dass er auf eine Art und Weise verwendet wird, mit der Sie nicht gerechnet haben. Die Zugriffsfunktionen werden jetzt vielleicht nur zu Testzwecken verwendet, aber wer weiß schon, was in der Zukunft passiert? Die Verwendung von Accessors anstelle des direkten Zugriffs auf Variablen gibt Ihnen viel mehr Flexibilität und ist zudem sehr kostengünstig.

Das andere Argument ist, dass die Verwendung von Accessoren anstelle von öffentlichen Mitgliedern eine gute Angewohnheit ist. Gute Gewohnheiten zu entwickeln, ist eine wichtige Fähigkeit eines Programmierers.

0 Stimmen

Um sicherzugehen, dass ich Sie verstehe, würde ich einen Accessor für die Variable double time erstellen: double time() const;

2 Stimmen

Ja, und zwar aus zwei Gründen: Erstens teilen Sie damit anderen Programmierern mit, was sie mit den Daten tun sollen, und zweitens kann der Compiler häufig const-Funktionen optimieren, was er ohne den const-Bezeichner nicht könnte.

2 Stimmen

Ach ja, beachten Sie, dass eine Variable time und eine Funktion time() nicht kompiliert werden; die meisten Leute haben die eigentliche Variable mit einem nachgestellten Unterstrich wie time_, und den Accessor wie time() { return time_; }

0voto

Jinuk Kim Punkte 705

Wie wäre es, den internen Zustand "geschützt" zu machen? Und dann tun unittest mit abgeleiteten Klasse.

0 Stimmen

Protected hat einen schlechteren Verkapselungsschutz als friend. Jeder die von Ihrer Klasse erben, rechtmäßigen Zugriff haben. Und weil Sie sie geschützt haben, sagen Sie, dass dies tatsächlich unterstützt wird. Zumindest eine Freundesklasse mit einem bestimmten Namen friend class MyObjectOnlyForTestingPurpose beschränkt den Zugang auf EINE Klasse mit EINEM bestimmten Namen. . . Alles in allem ist es so, als würde man einem Freund die Wohnungsschlüssel geben und nicht jedem in der Familie (einschließlich dem Halbbruder des Ehemanns der Tochter).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X