it-swarm.com.de

Soll ich private oder nur öffentliche Methoden testen?

Ich habe diesen Beitrag über das Testen privater Methoden gelesen. Normalerweise teste ich sie nicht, weil ich immer dachte, es sei schneller, nur öffentliche Methoden zu testen, die von außerhalb des Objekts aufgerufen werden. Testen Sie private Methoden? Soll ich sie immer testen?

300

Ich teste keine privaten Methoden. Eine private Methode ist ein Implementierungsdetail, das für die Benutzer der Klasse ausgeblendet werden sollte. Das Testen privater Methoden unterbricht die Kapselung.

Wenn ich finde, dass die private Methode groß oder komplex oder wichtig genug ist, um eigene Tests zu erfordern, stelle ich sie einfach in eine andere Klasse und mache sie dort öffentlich ( Method Object ). Dann kann ich problemlos die bisher private, aber jetzt öffentliche Methode testen, die jetzt von ihrer eigenen Klasse lebt.

289
jop

Was ist der Zweck des Testens?

Die Mehrheit der Antworten sagt bisher, dass private Methoden Implementierungsdetails sind, die nicht wichtig sind (oder zumindest nicht sollten), solange die öffentliche Schnittstelle gut getestet ist und funktioniert. Das ist absolut korrekt wenn Sie nur testen möchten, dass die öffentliche Schnittstelle funktioniert.

Ich persönlich werde hauptsächlich für Codetests eingesetzt, um sicherzustellen, dass zukünftige Codeänderungen keine Probleme verursachen, und meine Debugging-Bemühungen zu unterstützen, falls dies der Fall ist. Ich finde, dass das Testen der privaten Methoden genauso gründlich ist wie die öffentliche Schnittstelle (wenn nicht noch mehr!) Diesen Zweck fördert.

Bedenken Sie: Sie haben die öffentliche Methode A, die die private Methode B aufruft. A und B verwenden beide die Methode C. C wird geändert (vielleicht von Ihnen, vielleicht von einem Anbieter), wodurch A seine Tests nicht besteht. Wäre es nicht nützlich, auch Tests für B zu haben, auch wenn es privat ist, damit Sie wissen, ob das Problem in der Verwendung von C durch C, von B mit B oder von beiden liegt?

Das Testen privater Methoden bietet auch einen Mehrwert, wenn die Testabdeckung der öffentlichen Schnittstelle unvollständig ist. Während dies eine Situation ist, die wir im Allgemeinen vermeiden möchten, hängt die Effizienzprüfung der Einheit sowohl von den Tests, die Fehler finden, als auch von den Entwicklungs- und Wartungskosten dieser Tests ab. In einigen Fällen kann der Nutzen einer Testabdeckung von 100% als unzureichend angesehen werden, um die Kosten dieser Tests zu gewährleisten, wodurch Lücken in der Testabdeckung der öffentlichen Schnittstelle entstehen. In solchen Fällen kann ein gezielter Test einer privaten Methode eine sehr effektive Ergänzung der Codebasis sein.

270
Dave Sherohman

Ich neige dazu, den Ratschlägen von Dave Thomas und Andy Hunt in ihrem Buch Pragmatic Unit Testing zu folgen:

Im Allgemeinen möchten Sie keine Kapselung für .__ brechen. Testen (oder wie Mom immer sagte: "Setzen Sie Ihre Privaten nicht aus!"). Die meisten Von Zeit zu Zeit sollten Sie in der Lage sein, eine Klasse zu testen, indem Sie deren öffentliche Methoden. Wenn wichtige Funktionen verborgen sind Hinter einem privaten oder geschützten Zugriff kann dies ein Warnzeichen sein, dass Dort gibt es eine andere Klasse, die darum kämpft, herauszukommen.

Aber manchmal kann ich mich nicht davon abhalten, private Methoden zu testen, weil ich so die Gewissheit habe, dass ich ein vollständig robustes Programm baue.

136

Ich fühle mich irgendwie gezwungen, private Funktionen zu testen, da ich immer mehr einer unserer neuesten QS-Empfehlungen in unserem Projekt folge:

Nicht mehr als 10 in zyklomatische Komplexität pro Funktion.

Der Nebeneffekt der Durchsetzung dieser Richtlinie ist nun, dass viele meiner sehr großen öffentlichen Funktionen in eine viel fokussiertere, besser benannte private - Funktion aufgeteilt werden.
Die öffentliche Funktion ist (natürlich) immer noch da, wird aber im Wesentlichen reduziert, um all diese privaten 'Unterfunktionen' zu nennen.

Das ist wirklich cool, weil der Callstack jetzt viel einfacher zu lesen ist (anstelle eines Fehlers in einer großen Funktion habe ich einen Fehler in einer Unter-Unterfunktion mit dem Namen der vorherigen Funktionen im Callstack, um das Verständnis zu erleichtern 'wie ich dorthin kam')

Es scheint jedoch jetzt einfacher zu sein, die private - Funktionen direkt zu testen, und das Testen der großen öffentlichen Funktion einer Art Integrationstest überlassen, bei dem ein Szenario angegangen werden muss.

Nur meine 2 Cent.

58
VonC

Ja, ich teste private Funktionen, denn obwohl sie von Ihren öffentlichen Methoden getestet werden, ist es in TDD (Test Driven Design) ein Test, den kleinsten Teil der Anwendung zu testen. Auf private Funktionen kann jedoch nicht zugegriffen werden, wenn Sie sich in Ihrer Testklasse befinden. So testen wir unsere privaten Methoden.

Warum haben wir private Methoden?

Private Funktionen gibt es hauptsächlich in unserer Klasse, weil wir in unseren öffentlichen Methoden lesbaren Code erstellen wollen .. Wir möchten nicht, dass der Benutzer dieser Klasse diese Methoden direkt, sondern über unsere öffentlichen Methoden aufruft. Wir möchten auch nicht, dass sich ihr Verhalten ändert, wenn die Klasse erweitert wird (im Fall von protected), daher handelt es sich um eine private Klasse.

Beim Codieren verwenden wir testgetriebenes Design (TDD). Dies bedeutet, dass wir manchmal auf eine Funktionalität stoßen, die privat ist und testen möchte. Private Funktionen können in phpUnit nicht getestet werden, da wir in der Test-Klasse nicht auf sie zugreifen können (sie sind privat).

Wir denken hier sind 3 Lösungen:

1. Sie können Ihre Privaten durch Ihre öffentlichen Methoden testen

Vorteile

  • Unkompliziertes Testen von Einheiten (keine "Hacks" erforderlich)

Nachteile

  • Der Programmierer muss die öffentliche Methode verstehen, während er nur die private Methode testen möchte
  • Sie testen nicht den kleinsten testbaren Teil der Anwendung

2. Wenn das Private so wichtig ist, dann ist es vielleicht eine Codesmell, eine neue Klasse dafür zu erstellen

Vorteile

  • Sie können dies in eine neue Klasse umwandeln, denn wenn dies so wichtig ist, benötigen andere Klassen möglicherweise auch dies 
  • Die überprüfbare Einheit ist jetzt eine öffentliche Methode, also überprüfbar

Nachteile

  • Sie möchten keine Klasse erstellen, wenn sie nicht benötigt wird und nur von der Klasse verwendet wird, von der die Methode stammt
  • Möglicher Leistungsverlust durch zusätzlichen Aufwand

3. Ändern Sie den Zugriffsmodifizierer in (final) protected

Vorteile

  • Sie testen den kleinsten testbaren Teil der Anwendung. Wenn Final protected verwendet wird, kann die Funktion nicht überschrieben werden (nur wie bei einem privaten Benutzer).
  • Kein Leistungsverlust
  • Kein zusätzlicher Aufwand

Nachteile

  • Sie ändern einen privaten Zugriff in geschützt, das heißt, es ist für seine Kinder zugänglich
  • Sie benötigen immer noch eine Mock-Klasse in Ihrer Testklasse, um sie verwenden zu können

Beispiel

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

So kann unser Testgerät jetzt test_sleepWithSuspect aufrufen, um unsere bisherige private Funktion zu testen.

48
eddy147

Ich mag es aus mehreren Gründen nicht, private Funktionen zu testen. Sie sind wie folgt (dies sind die Hauptpunkte für die TLDR-Leute):

  1. Wenn Sie versucht sind, die private Methode einer Klasse zu testen, riecht es normalerweise nach Design.
  2. Sie können sie über die öffentliche Schnittstelle testen (so möchten Sie sie testen, da der Client sie so aufruft/verwendet). Sie können ein falsches Sicherheitsgefühl bekommen, indem Sie alle bestandenen Tests für Ihre privaten Methoden grünes Licht sehen. Es ist viel besser/sicherer, Edge-Fälle auf Ihren privaten Funktionen über Ihre öffentliche Schnittstelle zu testen.
  3. Sie riskieren schwerwiegende Testduplikationen (Tests, die sich sehr ähnlich sehen/anfühlen), indem Sie private Methoden testen. Dies hat große Konsequenzen, wenn sich die Anforderungen ändern, da viel mehr Tests als nötig brechen werden. Es kann Sie auch in eine Position versetzen, in der es aufgrund Ihrer Testsuite schwierig ist, Änderungen vorzunehmen. Dies ist die ultimative Ironie, da die Testsuite dazu dient, Sie sicher umzugestalten und umzugestalten.

Ich werde jede davon mit einem konkreten Beispiel erklären. Es stellt sich heraus, dass 2) und 3) etwas kompliziert miteinander verbunden sind, so dass ihr Beispiel ähnlich ist, obwohl ich sie als getrennte Gründe betrachte, warum Sie private Methoden nicht testen sollten.

Es gibt Zeiten, in denen es angebracht ist, private Methoden zu testen. Es ist nur wichtig, die oben aufgeführten Nachteile zu kennen. Ich werde später genauer darauf eingehen.

Ich gehe auch darüber nach, warum TDD keine gültige Entschuldigung ist, um ganz am Ende private Methoden zu testen.

Refactoring Ihren Weg aus einem schlechten Design

Eine der gebräuchlichsten (Anti) -Paternen, die ich sehe, ist das, was Michael Featherseine "Eisberg" -Klasse (Wenn Sie nicht wissen, wer Michael Feathers ist, kaufen Sie Ich lese sein Buch "Effektiv mit Legacy-Code arbeiten". Es gibt andere (Anti) Muster, die dazu führen, dass dieses Problem auftritt, aber dies ist bei weitem das häufigste, über das ich gestolpert bin. "Iceberg" -Klassen haben eine öffentliche Methode, und der Rest ist privat (weshalb es verlockend ist, die privaten Methoden zu testen). Es wird eine "Iceberg" -Klasse genannt, da normalerweise eine einzige öffentliche Methode auftaucht, der Rest der Funktionalität jedoch in Form von privaten Methoden unter Wasser verborgen ist. Es könnte ungefähr so ​​aussehen:

Rule Evaluator

Beispielsweise möchten Sie möglicherweise GetNextToken() testen, indem Sie es nacheinander für eine Zeichenfolge aufrufen und feststellen, dass es das erwartete Ergebnis zurückgibt. Eine Funktion wie diese erfordert einen Test: Dieses Verhalten ist nicht trivial, insbesondere wenn Ihre Tokenisierungsregeln komplex sind. Stellen wir uns vor, es ist nicht allzu komplex und wir wollen nur Tokens einschleusen, die durch den Weltraum abgegrenzt sind. Also schreibst du einen Test, vielleicht sieht er so aus (ein sprachunabhängiger Pseudocode, hoffentlich ist die Idee klar):

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

Naja, das sieht eigentlich ganz nett aus. Wir möchten sicherstellen, dass wir dieses Verhalten beibehalten, wenn wir Änderungen vornehmen. Aber GetNextToken() ist eine private Funktion! Wir können es also nicht so testen, weil es nicht einmal kompiliert wird (vorausgesetzt, wir verwenden eine Sprache, die tatsächlich public/private erzwingt, im Gegensatz zu einigen Skriptsprachen wie Python). Aber wie sieht es aus, wenn Sie die Klasse RuleEvaluator so ändern, dass sie dem Prinzip der Einzelverantwortung (Prinzip der Einzelverantwortung) folgt? Zum Beispiel scheinen wir einen Parser, Tokenizer und Evaluator in einer Klasse zu haben. Wäre es nicht besser, diese Verantwortlichkeiten einfach zu trennen? Wenn Sie darüber hinaus eine Tokenizer -Klasse erstellen, lauten die öffentlichen Methoden HasMoreTokens() und GetNextTokens(). Die Klasse RuleEvaluator könnte ein Tokenizer -Objekt als Mitglied haben. Jetzt können wir denselben Test wie oben beibehalten, außer dass wir die Klasse Tokenizer anstelle der Klasse RuleEvaluator testen.

So könnte es in UML aussehen:

Rule Evaluator Refactored

Beachten Sie, dass dieses neue Design die Modularität erhöht, sodass Sie diese Klassen möglicherweise in anderen Teilen Ihres Systems wiederverwenden können (bevor dies nicht möglich war, können private Methoden nicht per Definition wiederverwendet werden). Dies ist der Hauptvorteil einer Aufschlüsselung des RuleEvaluators sowie einer besseren Verständlichkeit/Lokalität.

Der Test würde sehr ähnlich aussehen, aber diesmal würde er tatsächlich kompiliert, da die Methode GetNextToken() für die Klasse Tokenizer jetzt öffentlich ist:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

Testen privater Komponenten über eine öffentliche Schnittstelle und Vermeiden von Testduplikationen

Auch wenn Sie nicht glauben, dass Sie Ihr Problem in weniger modulare Komponenten aufteilen können (was Sie in 95% der Fälle tun können, wenn Sie nur try), können Sie einfach das Private testen Funktionen über eine öffentliche Schnittstelle. Häufig sind private Mitglieder keine Tests wert, da sie über die öffentliche Schnittstelle getestet werden. Ich sehe oft Tests, die sehr ähnlich aussehen, aber zwei verschiedene Funktionen/Methoden testen. Wenn sich Anforderungen ändern (und dies immer tun), haben Sie jetzt 2 statt 1 defekte Tests. Und wenn Sie wirklich alle Ihre privaten Methoden getestet haben, haben Sie möglicherweise eher 10 statt 1 defekte Tests. Kurz gesagt, das Testen von privaten Funktionen (durch Verwendung von FRIEND_TEST Oder das Publizieren oder Reflektieren), die ansonsten über eine öffentliche Schnittstelle getestet werden könnten, kann zu Testduplikationen führen. Sie wollen das wirklich nicht, denn nichts tut mehr weh als Ihre Testsuite, die Sie verlangsamt. Es soll die Entwicklungszeit verkürzen und die Wartungskosten senken! Wenn Sie private Methoden testen, die ansonsten über eine öffentliche Schnittstelle getestet werden, kann die Testsuite genau das Gegenteil bewirken und die Wartungskosten sowie die Entwicklungszeit aktiv erhöhen. Wenn Sie eine private Funktion öffentlich machen oder wenn Sie etwas wie FRIEND_TEST Und/oder Reflektion verwenden, werden Sie es normalerweise auf lange Sicht bereuen.

Berücksichtigen Sie die folgende mögliche Implementierung der Klasse Tokenizer:

enter image description here

Angenommen, SplitUpByDelimiter() ist für die Rückgabe eines Arrays verantwortlich, sodass jedes Element im Array ein Token ist. Nehmen wir weiter an, GetNextToken() ist einfach ein Iterator über diesen Vektor. So könnte Ihr öffentlicher Test aussehen:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

Stellen wir uns vor, wir hätten das, was Michael Feather als tastendes Werkzeug bezeichnet. Mit diesem Tool können Sie die privaten Bereiche anderer Personen berühren. Ein Beispiel ist FRIEND_TEST Von googletest oder Reflektion, wenn die Sprache dies unterstützt.

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

Nehmen wir an, die Anforderungen ändern sich und die Tokenisierung wird viel komplexer. Sie entscheiden, dass ein einfaches Zeichenfolgentrennzeichen nicht ausreicht, und Sie benötigen eine Delimiter -Klasse, um den Job zu verarbeiten. Natürlich werden Sie damit rechnen, dass ein Test abbricht, aber dieser Schmerz nimmt zu, wenn Sie private Funktionen testen.

Wann kann es sinnvoll sein, private Methoden zu testen?

In der Software gibt es keine "Einheitsgröße". Manchmal ist es in Ordnung (und eigentlich ideal), "die Regeln zu brechen". Ich empfehle nachdrücklich, keine privaten Funktionen zu testen, wenn Sie können. Es gibt zwei Hauptsituationen, wenn ich denke, dass es in Ordnung ist:

  1. Ich habe intensiv mit Legacy-Systemen gearbeitet (weshalb ich so ein großer Michael Feathers-Fan bin), und ich kann mit Sicherheit sagen, dass es manchmal einfach am sichersten ist, nur die private Funktionalität zu testen. Es kann besonders hilfreich sein, um "Charakterisierungstests" in die Grundlinie zu bringen.

  2. Sie sind in Eile und müssen hier und jetzt das Schnellstmögliche tun. Auf lange Sicht möchten Sie keine privaten Methoden testen. Aber ich werde sagen, dass die Umgestaltung in der Regel einige Zeit in Anspruch nimmt, um Designprobleme zu lösen. Und manchmal muss man in einer Woche versenden. Das ist in Ordnung: Machen Sie es schnell und schmutzig und testen Sie die privaten Methoden mit einem Tastwerkzeug, wenn Sie der Meinung sind, dass dies der schnellste und zuverlässigste Weg ist, den Job zu erledigen. Verstehen Sie jedoch, dass das, was Sie getan haben, auf lange Sicht nicht optimal war, und ziehen Sie es in Betracht (oder beheben Sie es, wenn es vergessen wurde, aber Sie es später sehen).

Es gibt wahrscheinlich andere Situationen, in denen es in Ordnung ist. Wenn Sie denken, es ist in Ordnung und Sie haben eine gute Rechtfertigung, dann tun Sie es. Niemand hält dich auf. Seien Sie sich nur der potenziellen Kosten bewusst.

Die TDD-Ausrede

Abgesehen davon mag ich es nicht, wenn Leute TDD als Ausrede für das Testen privater Methoden verwenden. Ich übe TDD und ich glaube nicht, dass TDD Sie dazu zwingt. Sie können Ihren Test (für Ihre öffentliche Schnittstelle) zuerst schreiben und dann Code schreiben, um diese Schnittstelle zu erfüllen. Manchmal schreibe ich einen Test für eine öffentliche Schnittstelle, und ich befriedige ihn, indem ich auch eine oder zwei kleinere private Methoden schreibe (aber ich teste die privaten Methoden nicht direkt, aber ich weiß, dass sie funktionieren, oder mein öffentlicher Test würde fehlschlagen ). Wenn ich Edge-Fälle dieser privaten Methode testen muss, schreibe ich eine ganze Reihe von Tests, die sie über meine öffentliche Schnittstelle ausführen. Wenn Sie nicht herausfinden können, wie Sie die Edge-Fälle treffen, ist dies ein starkes Zeichen dafür, dass Sie kleine Komponenten mit jeweils eigenen öffentlichen Methoden umgestalten müssen. Dies ist ein Zeichen dafür, dass Ihre privaten Funktionen zu viel tun und außerhalb des Bereichs der Klasse liegen.

Manchmal stelle ich auch fest, dass ich einen Test schreibe, der zu groß ist, um ihn im Moment zu kauen, und so denke ich: "Ich werde später auf diesen Test zurückkommen, wenn ich mehr von einer API habe, mit der ich arbeiten kann" (I werde es auskommentieren und im Hinterkopf behalten). Hier werden dann viele Entwickler, die ich getroffen habe, Tests für ihre private Funktionalität schreiben und TDD als Sündenbock verwenden. Sie sagen: "Oh, nun, ich brauche einen anderen Test, aber um diesen Test zu schreiben, brauche ich diese privaten Methoden. Da ich keinen Produktionscode schreiben kann, ohne einen Test zu schreiben, muss ich einen Test schreiben für eine private Methode. " Aber was sie wirklich tun müssen, ist das Refactoring in kleinere und wiederverwendbare Komponenten, anstatt eine Reihe privater Methoden zu ihrer aktuellen Klasse hinzuzufügen/zu testen.

Hinweis:

Ich habe vor einiger Zeit eine ähnliche Frage zu Testen privater Methoden mit GoogleTest beantwortet. Ich habe diese Antwort größtenteils geändert, um hier sprachunabhängiger zu sein.

P.S. Hier ist der relevante Vortrag über Eisbergklassen und Suchwerkzeuge von Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJT

28

Ich denke, es ist am besten, die öffentliche Schnittstelle eines Objekts zu testen. Aus der Sicht der Außenwelt ist nur das Verhalten der öffentlichen Schnittstelle von Bedeutung, und darauf sollten Ihre Unit-Tests gerichtet sein.

Nachdem Sie einige solide Unit-Tests für ein Objekt geschrieben haben, möchten Sie diese Tests nicht ändern, nur weil sich die Implementierung hinter der Schnittstelle geändert hat. In dieser Situation haben Sie die Konsistenz Ihrer Gerätetests ruiniert.

23
17 of 26

Wenn die private Methode gut definiert ist (dh sie hat eine Funktion, die testbar ist und sich nicht im Laufe der Zeit ändern soll), dann yes. Ich teste alles, was getestet werden kann, wo es sinnvoll ist.

Beispielsweise kann eine Verschlüsselungsbibliothek die Tatsache verbergen, dass die Blockverschlüsselung mit einer privaten Methode durchgeführt wird, die jeweils nur 8 Byte verschlüsselt. Ich würde dafür einen Unit-Test schreiben - er soll sich nicht ändern, auch wenn er versteckt ist. Wenn er kaputt geht (zum Beispiel aufgrund zukünftiger Leistungsverbesserungen), möchte ich wissen, dass es die private Funktion ist, die kaputt ging, nicht nur dass eine der öffentlichen Funktionen brach.

Es beschleunigt später das Debuggen.

-Adam

19
Adam Davis

Wenn Ihre private Methode nicht getestet wird, indem Sie Ihre öffentlichen Methoden aufrufen, was tun Sie dann? __ Ich spreche privat, nicht geschützt oder Freund.

17
chrissie1

Wenn Sie testgesteuert (TDD) entwickeln, testen Sie Ihre privaten Methoden.

12
Jader Dias

Ich bin kein Experte auf diesem Gebiet, aber Unit-Tests sollten das Verhalten testen, nicht die Implementierung. Private Methoden sind ein strikter Bestandteil der Implementierung, daher sollte IMHO nicht getestet werden.

11
maxbog

Wir testen private Methoden durch Inferenz, womit ich meine, wir suchen nach einer Gesamtklassetestabdeckung von mindestens 95%, aber unsere Tests müssen nur in öffentliche oder interne Methoden abgerufen werden. Um die Abdeckung zu erhalten, müssen wir mehrere öffentliche und interne Anrufe anrufen, die auf den verschiedenen möglichen Szenarien basieren. Dies führt dazu, dass unsere Tests den Zweck des Codes, den sie testen, bewusster sind.

Trumpis Antwort auf den Beitrag, den Sie verlinkt haben, ist der beste.

11
Tom Carr

Ich glaube, Unit-Tests sind für das Testen öffentlicher Methoden gedacht. Ihre öffentlichen Methoden verwenden Ihre privaten Methoden, daher werden sie auch indirekt getestet.

8
scubabbl

Ich beschäftige mich seit einiger Zeit mit diesem Thema, vor allem mit dem Versuch, mich bei TDD zu versuchen.

Ich bin auf zwei Posts gestoßen, von denen ich denke, dass sie dieses Problem im Fall von TDD gründlich genug angehen.

  1. Testen privater Methoden, TDD und testgesteuertes Refactoring
  2. Testgetriebene Entwicklung wird nicht getestet

In Summe:

  • Wenn Sie testgetriebene Entwicklungstechniken (Design) verwenden, sollten private Methoden nur während des Re-Factoring-Prozesses von bereits funktionierendem und getestetem Code entstehen.

  • Aufgrund der Natur des Prozesses wird jedes Stück einfacher Implementierungsfunktionalität, das aus einer gründlich getesteten Funktion herausgezogen wurde, selbst getestet (d. H. Indirekte Testabdeckung).

Mir scheint es klar genug, dass die meisten Methoden zu Beginn der Codierung Funktionen auf höherer Ebene sein werden, da sie das Design einkapseln/beschreiben.

Daher sind diese Methoden öffentlich und das Testen wird leicht genug sein.

Die privaten Methoden werden später kommen, wenn alles gut funktioniert und wir neu strukturieren, um Lesbarkeit und Sauberkeit.

7
dkinzer

Wie oben zitiert: "Wenn Sie Ihre privaten Methoden nicht testen, woher wissen Sie dann, dass sie nicht kaputt gehen?"

Dies ist ein Hauptproblem. Einer der großen Punkte von Unit-Tests ist, zu wissen, wo, wann und wie ASAP gebrochen wurde. Dadurch sinkt der Aufwand für Entwicklung und QS erheblich. Wenn alles, was getestet wird, die Öffentlichkeit ist, haben Sie keine ehrliche Berichterstattung und Abgrenzung der Interna der Klasse.

Ich habe festgestellt, dass eine der besten Möglichkeiten dazu besteht, einfach die Testreferenz zum Projekt hinzuzufügen und die Tests in einer Klasse parallel zu den privaten Methoden zu platzieren. Fügen Sie die entsprechende Build-Logik ein, damit die Tests nicht in das endgültige Projekt integriert werden.

Dann haben Sie alle Vorteile, wenn diese Methoden getestet werden, und Sie können Probleme in Sekunden gegenüber Minuten oder Stunden finden.

Zusammenfassend, ja, Unit-Test Ihrer privaten Methoden.

6
Adron

Du solltest nicht . Wenn Ihre privaten Methoden ausreichend komplex sind und getestet werden müssen, sollten Sie sie einer anderen Klasse zuordnen. Behalte hohe Kohäsion , eine Klasse sollte nur einen Zweck haben. Die Klasse public interface sollte ausreichen.

5
fernandezdavid7

Wenn Sie Ihre privaten Methoden nicht testen, woher wissen Sie, dass sie nicht kaputt gehen?

4
Billy Jo

Es ist offensichtlich sprachabhängig. In der Vergangenheit habe ich mit c ++ die Testklasse zur Friend-Klasse erklärt. Leider erfordert dies, dass Ihr Produktionscode über die Testklasse informiert ist.

2
dvorak

Ich verstehe den Standpunkt, dass private Methoden als Implementierungsdetails betrachtet werden und nicht getestet werden müssen. Und ich würde bei dieser Regel bleiben, wenn wir uns nur außerhalb des Objekts entwickeln müssten. Aber sind wir eine Art eingeschränkte Entwickler, die sich nur außerhalb von Objekten entwickeln und nur ihre öffentlichen Methoden aufrufen? Oder entwickeln wir eigentlich auch dieses Objekt? Da wir nicht verpflichtet sind, Objekte außerhalb zu programmieren, müssen wir diese privaten Methoden wahrscheinlich in neuen öffentlichen Methoden aufrufen, die wir entwickeln. Wäre es nicht toll zu wissen, dass die private Methode allen Widersprüchen standhält?

Ich weiß, einige Leute könnten antworten, dass, wenn wir eine andere öffentliche Methode in dieses Objekt entwickeln, diese getestet werden sollte und das war es (die private Methode könnte ohne Test weiterleben). Dies gilt jedoch auch für alle öffentlichen Methoden eines Objekts: Beim Entwickeln einer Web-App werden alle öffentlichen Methoden eines Objekts von Controllermethoden aufgerufen und könnten daher als Implementierungsdetails für Controller betrachtet werden.

Warum machen wir Unit-Testobjekte? Denn es ist wirklich schwierig, nicht unmöglich zu sagen, um sicher zu sein, dass wir die Methoden der Controller mit der entsprechenden Eingabe testen, die alle Zweige des zugrunde liegenden Codes auslöst. Mit anderen Worten: Je höher wir im Stack sind, desto schwieriger ist es, das gesamte Verhalten zu testen. Dies gilt auch für private Methoden.

Für mich ist die Grenze zwischen privaten und öffentlichen Methoden ein psychologisches Kriterium, wenn es um Tests geht. Folgende Kriterien sind mir wichtiger: 

  • wird die Methode mehrmals von verschiedenen Orten aus aufgerufen?
  • ist die Methode so ausgereift, dass sie Tests erfordert?
2
Olivier Pichon

Wenn ich finde, dass die private Methode groß oder komplex oder wichtig genug ist, um eigene Tests zu erfordern, stelle ich sie einfach in eine andere Klasse und mache sie dort öffentlich (Method Object). Dann kann ich problemlos die zuvor private, aber jetzt öffentliche Methode testen, die jetzt von ihrer eigenen Klasse lebt.

1
Andy

Die Antwort auf "Soll ich private Methoden testen?" ist manchmal". Normalerweise sollten Sie mit der Schnittstelle Ihrer Klassen testen. 

  • Einer der Gründe ist, dass Sie für eine Funktion keine doppelte Abdeckung benötigen. 
  • Ein weiterer Grund besteht darin, dass Sie bei einem Wechsel privater Methoden jeden Test für sie aktualisieren müssen, auch wenn sich die Schnittstelle Ihres Objekts überhaupt nicht geändert hat.

Hier ist ein Beispiel:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

In RefactoredThing stehen Ihnen jetzt 5 Tests zur Verfügung, von denen Sie 2 zum Refactoring aktualisieren mussten, aber die Funktionalität Ihres Objekts hat sich wirklich nicht geändert. Nehmen wir also an, die Dinge sind komplexer und Sie haben eine Methode, die die Reihenfolge der Ausgabe definiert, wie zum Beispiel: 

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

Dies sollte nicht von einem externen Benutzer ausgeführt werden, aber Ihre einkapselnde Klasse ist möglicherweise zu schwer, um diese Logik immer wieder durchlaufen zu lassen. In diesem Fall möchten Sie dies vielleicht lieber in eine separate Klasse extrahieren, dieser Klasse eine Schnittstelle geben und dagegen testen.

Und schließlich sagen wir, dass Ihr Hauptobjekt sehr schwer ist und die Methode ziemlich klein ist und Sie wirklich sicherstellen müssen, dass die Ausgabe korrekt ist. Sie denken: "Ich muss diese private Methode testen!". Haben Sie das vielleicht können Sie Ihr Objekt leichter machen, indem Sie einige der schweren Arbeiten als Initialisierungsparameter übergeben? Dann kannst du etwas Leichteres passieren und dagegen testen.

0
unflores

Dabei geht es nicht nur um öffentliche oder private Methoden oder Funktionen, sondern auch um Details zur Implementierung. Private Funktionen sind nur ein Aspekt der Implementierungsdetails.

Unit-Testing ist immerhin ein White-Box-Testansatz. Wer beispielsweise die Coverage-Analyse verwendet, um Teile des Codes zu identifizieren, die bisher beim Testen vernachlässigt wurden, geht in die Implementierungsdetails ein.

A) Ja, Sie sollten Implementierungsdetails testen:

Stellen Sie sich eine Sortierfunktion vor, die aus Performancegründen eine private Implementierung von BubbleSort verwendet, wenn es bis zu 10 Elemente gibt, und eine private Implementierung eines anderen Sortieransatzes (z. B. heapsort), wenn mehr als 10 Elemente vorhanden sind. Die öffentliche API ist die einer Sortierfunktion. Ihre Testsuite nutzt jedoch besser das Wissen, dass tatsächlich zwei Sortieralgorithmen verwendet werden.

In diesem Beispiel könnten Sie die Tests mit der öffentlichen API durchführen. Dies würde jedoch eine Anzahl von Testfällen erfordern, die die Sortierfunktion mit mehr als 10 Elementen ausführen, so dass der Heapsort-Algorithmus ausreichend gut getestet wird. Allein das Vorhandensein solcher Testfälle weist darauf hin, dass die Testsuite mit den Implementierungsdetails der Funktion verbunden ist.

Wenn sich die Implementierungsdetails der Sortierfunktion ändern, kann dies dazu führen, dass die Grenze zwischen den beiden Sortieralgorithmen verschoben wird oder dass Heapsort durch Mergesort oder was auch immer ersetzt wird: Die vorhandenen Tests funktionieren weiterhin. Ihr Wert ist dennoch fragwürdig und sie müssen wahrscheinlich überarbeitet werden, um die geänderte Sortierfunktion besser testen zu können. Mit anderen Worten, es wird trotz der Tatsache, dass Tests auf der öffentlichen API ausgeführt wurden, ein Wartungsaufwand anfallen.

B) So testen Sie Details zur Implementierung

Ein Grund, warum viele Leute argumentieren, private Funktionen oder Implementierungsdetails sollten nicht getestet werden, ist die Tatsache, dass sich die Implementierungsdetails mit größerer Wahrscheinlichkeit ändern. Zumindest diese höhere Änderungswahrscheinlichkeit ist einer der Gründe, warum Implementierungsdetails hinter Schnittstellen versteckt werden.

Nehmen Sie nun an, dass die Implementierung hinter der Schnittstelle größere private Teile enthält, für die einzelne Tests der internen Schnittstelle eine Option darstellen könnten. Einige Leute argumentieren, diese Teile sollten nicht privat getestet werden, sie sollten in etwas Öffentliches umgewandelt werden. Sobald dieser Code öffentlich ist, wäre das Testen von Einheiten in Ordnung.

Das ist interessant: Während die Schnittstelle intern war, sollte sie sich wahrscheinlich ändern, da es sich um ein Implementierungsdetail handelt. Wenn Sie dieselbe Schnittstelle verwenden und sie öffentlich machen, führt dies zu einer magischen Transformation, dh zu einer Schnittstelle, die sich weniger wahrscheinlich ändert. Offensichtlich gibt es einige Mängel in dieser Argumentation.

Es gibt jedoch einige Wahrheit dahinter: Beim Testen von Implementierungsdetails, insbesondere unter Verwendung interner Schnittstellen, sollte man sich bemühen, Schnittstellen zu verwenden, die wahrscheinlich stabil bleiben. Ob eine Schnittstelle wahrscheinlich stabil sein wird, hängt jedoch nicht einfach davon ab, ob sie öffentlich oder privat ist. In den Projekten aus der ganzen Welt, in denen ich seit einiger Zeit arbeite, ändern sich häufig auch öffentliche Schnittstellen, und viele private Schnittstellen sind seit Ewigkeiten unangetastet.

Dennoch ist es eine gute Faustregel, die "Haustür zuerst" zu verwenden (siehe http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Denken Sie jedoch daran, dass es "Haustür zuerst" und nicht "Nur Haustür" heißt.

C) Zusammenfassung

Testen Sie auch die Implementierungsdetails. Testen Sie lieber an stabilen Schnittstellen (öffentlich oder privat). Wenn sich die Implementierungsdetails ändern, müssen auch die Tests der öffentlichen API überarbeitet werden. Wenn Sie etwas Privates in die Öffentlichkeit umwandeln, ändert sich die Stabilität nicht auf magische Weise.

0
Dirk Herrmann

Sie können Ihre Methode auch package-private machen, d. H. Default, und Sie sollten in der Lage sein, sie zu testen, es sei denn, es ist erforderlich, dass sie privat ist.

0
Techiee

Ein Hauptpunkt ist

Wenn wir testen, ob die Logik korrekt ist, und eine private Methode eine Logik enthält, sollten wir sie testen. Ist es nicht Warum werden wir das überspringen?

Das Schreiben von Tests basierend auf der Sichtbarkeit von Methoden ist völlig irrelevant. 

Umgekehrt

Auf der anderen Seite ist das Aufrufen einer privaten Methode außerhalb der ursprünglichen Klasse ein Hauptproblem. Außerdem gibt es Einschränkungen bei der Nachahmung einer privaten Methode in einigen Scheinwerkzeugen. (Bsp .: Mockito)

Obwohl es einige Tools wie Power Mock gibt, die dies unterstützen, ist dies eine gefährliche Operation. Der Grund ist, dass die JVM dazu gehackt werden muss.

Eine mögliche Problemumgehung ist (Wenn Sie Testfälle für private Methoden schreiben möchten)

Deklarieren Sie diese private -Methoden als protected. Es kann jedoch für mehrere Situationen unpraktisch sein.

0

Wenn die Methode signifikant genug/komplex genug ist, werde ich sie normalerweise "geschützt" machen und testen. Einige Methoden werden privat bleiben und im Rahmen von Komponententests für öffentliche/geschützte Methoden implizit getestet.

0
akapulko2020

Ja, Sie sollten nach Möglichkeit private Methoden testen. Warum? Um eine unnötige State Space Explosion von Testfällen zu vermeiden, die letztendlich implizit immer wieder dieselben privaten Funktionen an denselben Eingängen testen. Lassen Sie uns anhand eines Beispiels erklären, warum.

Betrachten Sie das folgende leicht erfundene Beispiel. Angenommen, wir möchten öffentlich eine Funktion verfügbar machen, die 3 Ganzzahlen akzeptiert und nur dann true zurückgibt, wenn diese 3 Ganzzahlen alle Primzahlen sind. Wir könnten es so implementieren:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

Wenn wir nun den strengen Ansatz verfolgen, dass nur öffentliche Funktionen getestet werden sollten, dürfen wir nur allPrime und nicht isPrime oder andAll testen.

Als Tester interessieren uns möglicherweise fünf Möglichkeiten für jedes Argument: < 0, = 0, = 1, prime > 1, not prime > 1. Um jedoch gründlich zu sein, müssten wir auch sehen, wie jede Kombination der Argumente zusammenspielt. Das sind also 5*5*5 = 125 Testfälle, die wir gemäß unserer Intuition gründlich testen müssen.

Wenn wir dagegen die privaten Funktionen testen könnten, könnten wir mit weniger Testfällen so viel Boden abdecken. Wir würden nur 5 Testfälle benötigen, um isPrime auf das gleiche Niveau wie unsere vorherige Intuition zu testen. Und nach der von Daniel Jackson vorgeschlagenen Small-Scope-Hypothese müssten wir die Funktion andAll nur bis zu einer geringen Länge testen, z. 3 oder 4. Das wären höchstens 16 weitere Tests. Also insgesamt 21 Tests. Anstelle von 125. Natürlich möchten wir wahrscheinlich einen wenige -Test für allPrime durchführen, aber wir fühlen uns nicht verpflichtet, alle 125 Kombinationen von Eingabeszenarien, die uns wichtig waren, vollständig abzudecken. Nur ein paar glückliche Wege.

Sicherlich ein erfundenes Beispiel, aber es war für eine klare Demonstration notwendig. Und das Muster erstreckt sich auf echte Software. Private Funktionen sind in der Regel die Bausteine ​​der untersten Ebene und werden daher häufig zu einer übergeordneten Logik kombiniert. Das heißt, auf höheren Ebenen gibt es aufgrund der verschiedenen Kombinationen mehr Wiederholungen von Sachen auf niedrigeren Ebenen.

0
Colm Bhandal

Ich sehe, dass viele Menschen in derselben Richtung denken: auf öffentlicher Ebene testen. Aber ist das nicht unser QA-Team? Sie testen die Eingabe und die erwartete Ausgabe. Wenn wir als Entwickler nur die öffentlichen Methoden testen, wiederholen wir einfach die Arbeit von QA und fügen keinen Wert durch "Komponententest" hinzu. 

0
aemorales1

Public vs. Private ist weder eine nützliche Unterscheidung für den Aufruf von APIs aus Ihren Tests, noch eine Unterscheidung zwischen Methode und Klasse. Die meisten testbaren Einheiten werden in einem Kontext angezeigt, in anderen jedoch ausgeblendet.

Was zählt, sind Deckung und Kosten. Sie müssen die Kosten minimieren, während Sie die Abdeckungsziele Ihres Projekts erreichen (Linie, Zweig, Pfad, Block, Methode, Klasse, Äquivalenzklasse, Anwendungsfall ... wie auch immer).

Verwenden Sie daher Tools, um die Abdeckung sicherzustellen, und gestalten Sie Ihre Tests so, dass sie (kurz- und langfristig) die geringsten Kosten verursachen. Manchmal erhalten Sie problemlos die gesamte Abdeckung, die Sie benötigen, indem Sie nur den Haupteinstiegspunkt der gesamten Anwendung testen. Häufiger reduzieren Sie die Kosten für die Abdeckung, indem Sie Tests an verschiedenen internen Einstiegspunkten durchführen. Dieses Konzept hört nicht bei der Sichtbarkeit der Methode auf.

Oft macht es ein gutes Moduldesign am billigsten, Tests mit Einstiegspunkten durchzuführen, die als öffentlich deklariert wurden, aber das ist nicht garantiert.

Privat gegen öffentlich spielt keine Rolle.

Verteuern Sie Tests nicht, indem Sie alle privaten Methoden direkt testen, und verteuern Sie Tests nicht, indem Sie direkte Aufrufe einiger Methoden vermeiden.

0
tkruse

Nein Sie sollten die privaten Methoden nicht testen warum? und außerdem bietet das beliebte Mocking-Framework wie Mockito keine Unterstützung für das Testen privater Methoden.

0
cammando

Ich verstehe nie das Konzept von Unit Test, aber jetzt weiß ich, worauf es ankommt.

Ein Unit-Test ist kein vollständiger Test . Es ist also kein Ersatz für QA und manuelle Tests. Das Konzept von TDD in diesem Aspekt ist falsch, da Sie nicht alles testen können, einschließlich privater Methoden, aber auch Methoden, die Ressourcen verwenden (insbesondere Ressourcen, die wir nicht kontrollieren können). TDD setzt alles daran, dass seine Qualität nicht erreicht werden konnte.

Bei einem Unit-Test handelt es sich eher um einen Pivot-Test Sie markieren einen beliebigen Pivot, und das Pivot-Ergebnis sollte gleich bleiben. 

0
magallanes