Soll ich private oder nur öffentliche Methoden testen? [geschlossen]


347

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?



"Soll ich private Helfer testen?" Ja. "Soll ich private Helfer direkt testen?" Es hängt im Allgemeinen davon ab, ob Sie sie einfach über die öffentliche Schnittstelle testen können. Warum sollten Sie sie direkt testen? Wenn es komplex wird, alle Aspekte von Helfern über eine öffentliche Schnittstelle zu testen, hat die Komponente dann ihre Existenz als einzelne Einheit überlebt?
Mihai Danila

Antworten:


328

Ich teste keine privaten Methoden. Eine private Methode ist ein Implementierungsdetail, das den Benutzern der Klasse verborgen bleiben sollte. Durch das Testen privater Methoden wird die Kapselung unterbrochen.

Wenn ich feststelle, dass die private Methode riesig oder komplex oder wichtig genug ist, um eigene Tests zu erfordern, stelle ich sie einfach in eine andere Klasse und mache sie dort öffentlich ( Methodenobjekt ). Dann kann ich leicht die zuvor private, aber jetzt öffentliche Methode testen, die jetzt in einer eigenen Klasse lebt.


88
Ich stimme dir nicht zu. Idealerweise schreiben Sie einen Schnelltest, bevor Sie mit dem Codieren einer Funktion beginnen. Denken Sie an typische Eingaben und die Ausgabe. Schreiben Sie den Test (der nicht länger als ein paar Sekunden dauern sollte) und codieren Sie, bis der Test richtig ist. Es gibt keinen Grund, diesen Arbeitsstil für private Methoden aufzugeben.
Frank

254
Zu sagen, dass private Methoden nicht getestet werden müssen, ist wie zu sagen, dass ein Auto in Ordnung ist, solange es in Ordnung fährt, und es spielt keine Rolle, was sich unter der Motorhaube befindet. Aber wäre es nicht schön zu wissen, dass sich ein Kabel im Inneren langsam löst - auch wenn der Benutzer nichts bemerkt? Sicher, Sie können alles öffentlich machen, aber worum geht es? Sie werden immer einige private Methoden wollen.
Frank

37
"Eine private Methode ist ein Implementierungsdetail, das den Benutzern der Klasse verborgen bleiben sollte." Aber befinden sich Tests wirklich auf derselben Seite der Klassenoberfläche wie die "normalen" (Laufzeit-) Benutzer? ;)
mlvljr

34
Die Gefahr, dass Sie alles, was Sie testen möchten, in eine andere Klasse ziehen, besteht darin, dass Sie möglicherweise Ihr Produkt überentwickeln und über eine Million wiederverwendbarer Komponenten verfügen, die niemals wiederverwendet werden.
Occulus

44
Das Vergleichen eines Codes mit einem Auto ist falsch. Code nicht ' mit der Zeit schlecht , er ist ewig . Wenn Sie die öffentliche Schnittstelle nur testen, um festzustellen, ob sie in Ordnung ist , ist das Testen des öffentlichen Codes unzureichend. In diesem Fall wird das separate Testen privater Methoden den Gesamttest nicht abschließen, egal wie sehr Sie es versuchen. Konzentrieren Sie sich darauf, Ihren öffentlichen Code als Ganzes ausführlich zu testen, und nutzen Sie das Wissen über die internen Abläufe des Codes, um die richtigen Szenarien zu erstellen.
Rustyx

293

Was ist der Zweck des Testens?

Die Mehrheit der bisherigen Antworten besagt, dass private Methoden Implementierungsdetails sind, die keine Rolle spielen (oder zumindest nicht spielen sollten), solange die öffentliche Schnittstelle gut getestet ist und funktioniert. Das ist absolut richtig, wenn Sie nur testen möchten, ob die öffentliche Schnittstelle funktioniert .

Persönlich besteht meine Hauptanwendung für Codetests darin, sicherzustellen, dass zukünftige Codeänderungen keine Probleme verursachen, und meine Debugging-Bemühungen zu unterstützen, wenn dies der Fall ist. Ich finde, dass das Testen der privaten Methoden genauso gründlich ist wie die öffentliche Schnittstelle (wenn nicht mehr!) Diesen Zweck unterstützt.

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 (möglicherweise von Ihnen, möglicherweise von einem Anbieter), wodurch A beginnt, seine Tests nicht zu bestehen. Wäre es nicht nützlich, auch Tests für B durchzuführen, obwohl diese privat sind, damit Sie wissen, ob das Problem in der Verwendung von C durch A, der Verwendung von C durch B oder beidem liegt?

Das Testen privater Methoden bietet auch dann 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 das Testen der Effizienzeinheiten sowohl von den Tests ab, bei denen Fehler festgestellt werden, als auch von den damit verbundenen Entwicklungs- und Wartungskosten dieser Tests. In einigen Fällen können die Vorteile einer 100% igen Testabdeckung als unzureichend beurteilt werden, um die Kosten dieser Tests zu rechtfertigen, was zu Lücken in der Testabdeckung der öffentlichen Schnittstelle führt. In solchen Fällen kann ein gezielter Test einer privaten Methode eine sehr effektive Ergänzung der Codebasis sein.


72
Das Problem hierbei ist, dass diese "zukünftigen Codeänderungen" ausnahmslos bedeuten, das Innenleben einer Klasse neu zu gestalten. Dies passiert so oft, dass das Schreiben von Tests ein Hindernis für das Refactoring darstellt.
Outlaw Programmer

40
Wenn Sie Ihre Komponententests ständig ändern, haben Sie die Konsistenz Ihrer Tests verloren und können sogar Fehler in den Komponententests selbst verursachen.
17 vom 26.

6
@ 17 Wenn die Tests und die Implementierung synchron geändert werden (wie es scheint. Es sollte so sein), gibt es viel weniger Probleme.
mlvljr

11
@Sauronlord, Der Grund, warum Sie private Methoden testen, liegt darin, dass wir nicht direkt wissen, wo die Hauptursache für den Fehler liegt,testDoSomething()testDoSomethingPrivate() wenn Sie nur die öffentlichen Methoden testen und der Test fehlschlägt. Es könnte entweder oder sein . Dies macht den Test weniger wertvoll. . Hier sind weitere Gründe für das Testen von Private Stackoverflow.com/questions/34571/… :
Pacerier

3
@Pacerier Es gibt auch einen Unterschied zwischen dem Testen Ihres Codes und einem kontinuierlichen automatisierten Testprozess. Sie sollten natürlich sicherstellen, dass Ihre private Methode funktioniert, aber Sie sollten keine Tests haben, die Sie mit der privaten Methode koppeln, da dies nicht Teil des Anwendungsfalls der Software ist.
Didier A.

150

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 zum Testen aufheben (oder wie Mama immer sagte: "Machen Sie Ihre Privaten nicht sichtbar!"). Meistens sollten Sie in der Lage sein, eine Klasse zu testen, indem Sie ihre öffentlichen Methoden anwenden. Wenn es bedeutende Funktionen gibt, die hinter privaten oder geschützten Zugriffen verborgen sind, könnte dies ein Warnsignal dafür sein, dass sich dort eine andere Klasse befindet, die Schwierigkeiten hat, herauszukommen.

Aber manchmal kann ich mich nicht davon abhalten, private Methoden zu testen, weil es mir das Gefühl gibt, ein völlig robustes Programm zu erstellen.


9
Ich würde empfehlen, Unit-Tests zu deaktivieren, die auf private Methoden abzielen. Sie sind eine Codekopplung und belasten zukünftige Refactoring-Arbeiten oder behindern manchmal sogar das Hinzufügen oder Ändern von Features. Es ist gut, einen Test für sie zu schreiben, während Sie sie implementieren, um automatisch zu bestätigen, dass Ihre Implementierung funktioniert, aber es ist nicht vorteilhaft, die Tests als Regression beizubehalten.
Didier A.

61

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

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

Der Nebeneffekt der Durchsetzung dieser Richtlinie besteht nun darin, dass viele meiner sehr großen öffentlichen Funktionen in viel fokussiertere, besser benannte private Funktionen unterteilt werden.
Die öffentliche Funktion ist (natürlich) immer noch vorhanden, wird jedoch im Wesentlichen darauf reduziert, alle diese privaten "Unterfunktionen" zu nennen.

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

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

Nur meine 2 Cent.


2
Um auf @jop zu reagieren, muss ich diese privaten Funktionen (die aufgrund der Aufteilung einer großen, zu zyklomatischen, komplexen öffentlichen Funktion erstellt wurden) nicht in eine andere Klasse exportieren. Ich mag es, wenn sie immer noch eng mit der öffentlichen Funktion in derselben Klasse verbunden sind. Aber immer noch Unit-getestet.
VonC

2
Ich habe die Erfahrung gemacht, dass diese privaten Methoden nur Dienstprogrammmethoden sind, die von diesen öffentlichen Methoden wiederverwendet werden. Manchmal ist es bequemer, die ursprüngliche Klasse in zwei (oder drei) zusammenhängendere Klassen aufzuteilen, um diese privaten Methoden in ihren eigenen Klassen öffentlich zu machen und daher testbar zu machen.
Jop

7
Nein, in meinem Fall sind diese neuen privaten Funktionen wirklich Teil des größeren Algorithmus, der durch die öffentliche Funktion dargestellt wird. Diese Funktion ist in kleinere Teile unterteilt, die nicht nützlich sind, sondern Schritte eines größeren Prozesses. Daher die Notwendigkeit, sie einem Unit-Test zu unterziehen (anstatt den gesamten Algo auf einmal einem Unit-Test zu unterziehen)
VonC

Für diejenigen, die an zyklomatischer Komplexität interessiert sind, habe ich eine Frage zum Thema hinzugefügt: stackoverflow.com/questions/105852/…
VonC

Hoppla, die URL der Frage hat sich aufgrund eines Tippfehlers im Titel geändert! stackoverflow.com/questions/105852/…
VonC

51

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

Warum haben wir private Methoden?

Private Funktionen existieren hauptsächlich in unserer Klasse, weil wir lesbaren Code in unseren öffentlichen Methoden erstellen möchten. Wir möchten nicht, dass der Benutzer dieser Klasse diese Methoden direkt aufruft, sondern über unsere öffentlichen Methoden. Außerdem möchten wir ihr Verhalten beim Erweitern der Klasse nicht ändern (im Fall von geschützt), daher ist es eine private.

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

Wir denken hier sind 3 Lösungen:

1. Sie können Ihre Privaten mit Ihren öffentlichen Methoden testen

Vorteile

  • Einfacher Unit-Test (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, ist es vielleicht ein Code-Geruch, eine neue separate Klasse dafür zu erstellen

Vorteile

  • Sie können dies in eine neue Klasse umgestalten, denn wenn es so wichtig ist, benötigen es möglicherweise auch andere Klassen
  • Die testbare Einheit ist jetzt eine öffentliche Methode, also testbar

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 aufgrund zusätzlichen Overheads

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

Vorteile

  • Sie testen den kleinsten testbaren Teil der Anwendung. Bei Verwendung von final protected kann die Funktion nicht überschrieben werden (genau wie bei einer privaten).
  • Kein Leistungsverlust
  • Kein zusätzlicher Aufwand

Nachteile

  • Sie ändern einen privaten Zugriff auf geschützt, was bedeutet, dass er für seine Kinder zugänglich ist
  • Sie benötigen 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);
  }
}

Unsere Testeinheit kann jetzt test_sleepWithSuspect aufrufen, um unsere frühere private Funktion zu testen.


eddy147, ich mag das Konzept, geschützte Methoden über Mocks zu testen, sehr . Vielen Dank!!!!
Theodore R. Smith

15
Ich möchte nur darauf hinweisen, dass in der ursprünglichen Beschreibung von TDD beim Testen von Einheiten die Einheit die Klasse ist , keine Methode / Funktion. Wenn Sie also "Testen des kleinsten Teils der Anwendung" erwähnen, ist es falsch , den kleinsten testbaren Teil als Methode zu bezeichnen. Wenn Sie diese Logik verwenden, können Sie auch eine einzelne Codezeile anstelle eines ganzen Codeblocks sprechen.
Matt Quigley

@Matt Eine Arbeitseinheit kann auf eine Klasse verweisen, aber auch auf eine einzelne Methode.
eddy147

4
@ eddy147 Unit Testing kommt Test Driven Development, wo Unit als Klasse definiert wurde. Wie bei The Internet hat sich die Semantik erweitert, um eine ganze Reihe von Dingen zu bedeuten (dh fragen Sie 2 Personen, was der Unterschied zwischen Unit- und Integrationstests ist, und Sie erhalten 7 Antworten). TDD war als eine Möglichkeit gedacht, Software mit SOLID-Prinzipien zu schreiben, einschließlich Einzelverantwortung, bei der eine Klasse eine Einzelverantwortung hatte und keine hohe zyklische Komplexität aufweisen sollte. In TDD schreiben Sie Ihre Klasse und testen beide Einheiten zusammen. Private Methoden sind gekapselt und haben keinen entsprechenden Komponententest.
Matt Quigley

"Wenn wir codieren, verwenden wir Test-Driven-Design (TDD). Dies bedeutet, dass wir manchmal auf eine Funktionalität stoßen, die privat ist und testen möchte." Ich bin mit dieser Aussage nicht einverstanden. Weitere Einzelheiten finden Sie in meiner Antwort unten. TDD bedeutet nicht, dass Sie gezwungen sind, private Methoden zu testen. Sie können private Methoden testen: und das ist Ihre Wahl, aber es ist nicht TDD, das Sie dazu bringt, so etwas zu tun.
Matt Messersmith

41

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, ist dies normalerweise ein Designgeruch.
  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 grünes Licht für alle bestandenen Tests für Ihre privaten Methoden sehen. Es ist viel besser / sicherer, Edge Cases für Ihre privaten Funktionen über Ihre öffentliche Schnittstelle zu testen.
  3. Sie riskieren schwere Testduplikationen (Tests, die sehr ähnlich aussehen / sich sehr ähnlich anfühlen), indem Sie private Methoden testen. Dies hat erhebliche Konsequenzen, wenn sich die Anforderungen ändern, da viel mehr Tests als erforderlich unterbrochen werden. Es kann Sie auch in eine Position bringen, in der es aufgrund Ihrer Testsuite schwierig ist, eine Umgestaltung vorzunehmen ... was die ultimative Ironie ist, da die Testsuite Ihnen dabei hilft, sicher neu zu gestalten und umzugestalten!

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

Es ist manchmal angebracht, private Methoden zu testen. Es ist nur wichtig, sich der oben aufgeführten Nachteile bewusst zu sein. Ich werde später genauer darauf eingehen.

Ich gehe auch darauf ein, warum TDD am Ende keine gültige Entschuldigung für das Testen privater Methoden ist.

Umgestaltung Ihres Weges aus einem schlechten Design

Eines der häufigsten (Anti) Muster, das ich sehe, ist das, was Michael Feathers eine "Eisberg" -Klasse nennt (wenn Sie nicht wissen, wer Michael Feathers ist, kaufen / lesen Sie sein Buch "Effektiv mit Legacy-Code arbeiten" eine wissenswerte Person, wenn Sie ein professioneller Softwareentwickler / -entwickler sind). 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 als "Eisberg" -Klasse bezeichnet, da normalerweise eine einzelne öffentliche Methode auftaucht, der Rest der Funktionalität jedoch in Form privater Methoden unter Wasser verborgen ist.

Regelauswerter

Beispielsweise möchten Sie möglicherweise testen, GetNextToken()indem Sie eine Zeichenfolge nacheinander aufrufen und feststellen, dass das erwartete Ergebnis zurückgegeben wird. Eine solche Funktion erfordert einen Test: Dieses Verhalten ist nicht trivial, insbesondere wenn Ihre Tokenisierungsregeln komplex sind. Stellen wir uns vor, es sei nicht allzu komplex, und wir wollen nur Token einbinden, die durch den Raum begrenzt sind. Sie schreiben also einen Test, vielleicht sieht er ungefähr 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;
}

Nun, das sieht eigentlich ganz gut aus. Wir möchten sicherstellen, dass wir dieses Verhalten beibehalten, wenn wir Änderungen vornehmen. Ist GetNextToken()aber eine private Funktion! Daher können wir es nicht so testen, da es nicht einmal kompiliert wird (vorausgesetzt, wir verwenden eine Sprache, die im Gegensatz zu einigen Skriptsprachen wie Python tatsächlich öffentlich / privat erzwingt). Aber wie wäre es, die RuleEvaluatorKlasse so zu ändern , dass sie dem Prinzip der Einzelverantwortung (Prinzip der Einzelverantwortung) folgt? Zum Beispiel scheinen Parser, Tokenizer und Evaluator in einer Klasse zusammengefasst zu sein. Wäre es nicht besser, diese Verantwortlichkeiten einfach zu trennen? Wenn Sie eine TokenizerKlasse erstellen , sind die öffentlichen Methoden außerdem HasMoreTokens()und GetNextTokens(). Die RuleEvaluatorKlasse könnte eine habenTokenizerObjekt als Mitglied. Jetzt können wir den gleichen Test wie oben beibehalten, außer dass wir die TokenizerKlasse anstelle der RuleEvaluatorKlasse testen .

So könnte es in UML aussehen:

Regelauswerter überarbeitet

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 Sie dies nicht konnten, können private Methoden per Definition nicht wiederverwendet werden). Dies ist der Hauptvorteil des Ausfalls des RuleEvaluator zusammen mit einer besseren Verständlichkeit / Lokalität.

Der Test würde sehr ähnlich aussehen, außer dass er diesmal tatsächlich kompiliert würde, da die GetNextToken()Methode jetzt für die TokenizerKlasse ö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

Selbst 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 es nur versuchen ), können Sie die privaten Funktionen einfach über eine öffentliche Schnittstelle testen. Oft sind private Mitglieder keinen Test wert, da sie über die öffentliche Schnittstelle getestet werden. Oft sehe ich Tests, die aussehen sehr gut ähnlich , aber zwei verschiedene Funktionen / Methoden testen. Was am Ende passiert, ist, dass Sie, wenn sich die Anforderungen ändern (und dies immer tun), jetzt 2 fehlerhafte Tests anstelle von 1 haben. Und wenn Sie wirklich alle Ihre privaten Methoden getestet haben, haben Sie möglicherweise eher 10 fehlerhafte Tests anstelle von 1. Kurz gesagt Testen privater Funktionen (mithilfe vonFRIEND_TESToder sie öffentlich zu machen oder Reflexion zu verwenden), die andernfalls über eine öffentliche Schnittstelle getestet werden könnten, kann zu Testduplikationen führen und / oder Reflexion verwenden, werden Sie es auf lange Sicht normalerweise bereuen.. 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 und die Entwicklungszeit aktiv erhöhen. Wenn Sie eine private Funktion öffentlich machen oder wenn Sie so etwas verwendenFRIEND_TEST

Betrachten Sie die folgende mögliche Implementierung der TokenizerKlasse:

Geben Sie hier die Bildbeschreibung ein

Angenommen, dies SplitUpByDelimiter()ist für die Rückgabe eines Arrays verantwortlich, sodass jedes Element im Array ein Token ist. Sagen wir einfach, das GetNextToken()ist einfach ein Iterator über diesen Vektor. Ihr öffentlicher Test könnte also so 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_TESTvon googletest oder Reflexion, wenn die Sprache es 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 jetzt an, die Anforderungen ändern sich und die Tokenisierung wird viel komplexer. Sie entscheiden, dass ein einfaches Zeichenfolgenbegrenzer nicht ausreicht, und Sie benötigen einDelimiter Klasse, um den Job zu erledigen. Natürlich erwarten Sie, dass ein Test abbricht, aber dieser Schmerz nimmt zu, wenn Sie private Funktionen testen.

Wann können private Methoden getestet werden?

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

  1. Ich habe ausgiebig 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. Dies kann besonders hilfreich sein, um "Charakterisierungstests" in die Grundlinie aufzunehmen.

  2. Sie sind in Eile und müssen das Schnellste tun, was hier und jetzt möglich ist. Auf lange Sicht möchten Sie keine privaten Methoden testen. Aber ich werde sagen, dass es normalerweise einige Zeit dauert, um Designprobleme zu beheben. 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 tastenden Werkzeug, wenn dies Ihrer Meinung nach der schnellste und zuverlässigste Weg ist, um die Arbeit zu erledigen. Aber verstehen Sie, dass das, was Sie getan haben, auf lange Sicht nicht optimal war, und denken Sie darüber nach, darauf zurückzukommen (oder, wenn es vergessen wurde, aber Sie es später sehen, beheben Sie es).

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

Die TDD-Entschuldigung

Abgesehen davon mag ich es wirklich 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 zuerst Ihren Test (für Ihre öffentliche Schnittstelle) schreiben und dann Code schreiben, um diese Schnittstelle zu erfüllen. Manchmal schreibe ich einen Test für eine öffentliche Schnittstelle und erfülle 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 fehlschlagen würde ). Wenn ich Randfälle dieser privaten Methode testen muss, schreibe ich eine ganze Reihe von Tests, die sie über meine öffentliche Schnittstelle treffen.Wenn Sie nicht herausfinden können, wie Sie die Randfälle treffen können, ist dies ein starkes Zeichen dafür, dass Sie kleine Komponenten mit jeweils eigenen öffentlichen Methoden umgestalten müssen. Es ist ein Zeichen dafür, dass Ihre privaten Funktionen zu viel tun und außerhalb des Bereichs der Klasse liegen .

Manchmal finde ich auch, dass ich einen Test schreibe, der im Moment zu groß ist, um ihn zu kauen, und deshalb denke ich, "eh, ich werde später auf diesen Test zurückkommen, wenn ich mehr API zum Arbeiten habe" (I. Ich werde es auskommentieren und im Hinterkopf behalten. Hier werden viele Entwickler, die ich getroffen habe, dann 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. " Was sie jedoch wirklich tun müssen, ist die Umgestaltung in kleinere und wiederverwendbare Komponenten, anstatt ihrer aktuellen Klasse eine Reihe privater Methoden hinzuzufügen / zu testen.

Hinweis:

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

PS Hier ist die relevante Vorlesung über Eisbergklassen und Greifwerkzeuge von Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU


Das Problem, das ich bei der Auflistung "Sie könnten diese Klassen möglicherweise in anderen Teilen Ihres Systems wiederverwenden" als Vorteil habe, ist, dass der Grund, warum ich eine Funktion als privat markiere, manchmal darin besteht, dass ich nicht möchte, dass sie von anderen Teilen von verwendet wird das System. Dies ist ein sprachspezifisches Problem: Idealerweise wäre dies für ein "Modul" privat, aber wenn die Sprache dies nicht unterstützt (z. B. PHP), repräsentiert meine Klasse das Modul, nicht die Einheit: Die privaten Methoden sind wiederverwendbarer Code mit eigenen Verträgen, dürfen aber nur innerhalb dieser Klasse wiederverwendet werden.
IMSoP

Ich verstehe, was Sie sagen, aber ich mag die Art und Weise, wie die Python-Community mit diesem Problem umgeht. Wenn Sie das betreffende "private" Mitglied mit einem führenden Namen benennen _, bedeutet dies "hey, dies ist" privat ". Sie können es verwenden, aber vollständige Offenlegung, es wurde nicht für die Wiederverwendung entwickelt und Sie sollten es nur verwenden, wenn Sie es wirklich tun weiß was du tust ". Sie können in jeder Sprache den gleichen Ansatz verfolgen: Machen Sie diese Mitglieder öffentlich, markieren Sie sie jedoch mit einem führenden Zeichen _. Oder vielleicht sollten diese Funktionen wirklich privat sein und nur über eine öffentliche Schnittstelle getestet werden (siehe Antwort für weitere Details). Es ist von Fall zu Fall keine allgemeine Regel
Matt Messersmith

26

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

Sobald Sie einige solide Komponententests für ein Objekt geschrieben haben, möchten Sie nicht mehr zurückgehen und diese Tests ändern müssen, nur weil sich die Implementierung hinter der Schnittstelle geändert hat. In dieser Situation haben Sie die Konsistenz Ihrer Unit-Tests ruiniert.


21

Wenn Ihre private Methode nicht durch Aufrufen Ihrer öffentlichen Methoden getestet wird, was macht sie dann? Ich spreche privat nicht geschützt oder Freund.


3
Vielen Dank. Dies ist ein überraschend unterschätzter Kommentar und besonders relevant, auch nach fast 8 Jahren seit seiner Abfassung.
Sauronlord

1
Mit der gleichen Überlegung könnte man argumentieren, nur Software von der Benutzeroberfläche aus zu testen (Testen auf Systemebene), da irgendwie jede Funktion in der Software irgendwie von dort ausgeführt würde.
Dirk Herrmann

18

Wenn die private Methode gut definiert ist (dh eine Funktion hat, die testbar ist und sich im Laufe der Zeit nicht ändern soll), dann ja. Ich teste alles, was testbar ist, wo es Sinn macht.

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

Es beschleunigt das spätere Debuggen.

-Adam


1
Wäre es in diesem Fall nicht sinnvoll, diese private Methode in eine andere Klasse zu verschieben und sie dann nur öffentlich oder öffentlich statisch zu machen?
Outlaw Programmer

1 Wenn Sie testen Ihre privaten Member - Funktionen und den Test der öffentlichen Schnittstelle nicht ausfällt alle ein Ergebnis , das Sie erhalten, die äquivalent ist etwas gebrochen ist , ohne eine Ahnung , was das ist etwas.
Olumide

12

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


2
Sie würden die privaten Methoden beim Refactoring von agiletips.blogspot.com/2008/11/…
Josh Johnson

4
Nicht wahr, Sie testen Ihre öffentlichen Methoden und sobald die Tests bestanden sind, extrahieren Sie den Code in Ihren öffentlichen Methoden in private Methoden während des "Reinigungs" -Schritts. Das Testen privater Methoden ist imo eine schlechte Idee, da es die Änderung der Implementierung erschwert (was ist, wenn Sie eines Tages ändern möchten, wie Sie etwas tun, sollten Sie in der Lage sein, es zu ändern und alle Ihre Tests auszuführen, und wenn Ihre neue Art, dies zu tun) Die Sache ist richtig, sie sollten bestehen, ich möchte nicht alle meine privaten Tests dafür ändern müssen.
Tesseract

1
@Tesseract, wenn ich Ihren Kommentar mehr als einmal positiv bewerten könnte. "... Sie sollten in der Lage sein, es zu ändern und alle Ihre Tests auszuführen, und wenn Ihre neue Vorgehensweise korrekt ist, sollten sie bestehen." DAS ist einer der Hauptvorteile von Komponententests. Sie ermöglichen es Ihnen, mit Vertrauen umzugestalten. Sie können die inneren privaten Abläufe Ihrer Klasse vollständig ändern und (ohne alle Unit-Tests neu zu schreiben) darauf vertrauen, dass Sie nichts kaputt gemacht haben, da alle (vorhandenen) Unit-Tests (auf Ihrer öffentlichen Schnittstelle) noch bestanden sind.
Lee

Nicht einverstanden, siehe meine Antwort unten
Matt Messersmith

11

Ich bin kein Experte auf diesem Gebiet, aber Unit-Tests sollten das Verhalten und nicht die Implementierung testen. Private Methoden sind ausschließlich Teil der Implementierung, daher sollte IMHO nicht getestet werden.


Wo wird die Implementierung dann getestet? Wenn einige Funktionen das Caching verwenden, handelt es sich dann um ein Implementierungsdetail, und das Caching wird nicht getestet?
Dirk Herrmann

11

Wir testen private Methoden durch Inferenz, womit ich meine, dass wir eine Gesamttestabdeckung von mindestens 95% anstreben, aber unsere Tests nur öffentliche oder interne Methoden aufrufen. Um die Abdeckung zu erhalten, müssen wir mehrere Anrufe bei der Öffentlichkeit / den Interna tätigen, basierend auf den verschiedenen Szenarien, die auftreten können. Dies macht unsere Tests zielgerichteter in Bezug auf den Zweck des Codes, den sie testen.

Trumpis Antwort auf den von Ihnen verlinkten Beitrag ist die beste.


9

Unit-Tests dienen meiner Meinung nach zum Testen öffentlicher Methoden. Ihre öffentlichen Methoden verwenden Ihre privaten Methoden, sodass sie indirekt auch getestet werden.


7

Ich habe mich eine Weile mit diesem Thema beschäftigt, vor allem, weil ich mich bei TDD versucht habe.

Ich bin auf zwei Beiträge gestoßen, die meines Erachtens dieses Problem im Fall von TDD gründlich genug angehen.

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

Zusammenfassend:

  • Bei Verwendung testgetriebener Entwicklungstechniken (Design) sollten private Methoden nur während des Re-Factoring-Prozesses von bereits funktionierendem und getestetem Code entstehen.

  • Aufgrund der Natur des Prozesses wird jede einfache Implementierungsfunktionalität, die aus einer gründlich getesteten Funktion extrahiert wird, selbst getestet (dh indirekte Testabdeckung).

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

Daher werden diese Methoden öffentlich sein und das Testen wird einfach genug sein.

Die privaten Methoden werden später verfügbar sein, sobald alles gut funktioniert und wir sie aus Gründen der Lesbarkeit und Sauberkeit neu faktorisieren .


6

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

Dies ist ein großes Problem. Einer der großen Punkte bei Unit-Tests ist zu wissen, wo, wann und wie etwas so schnell wie möglich kaputt gegangen ist. Dadurch wird ein erheblicher Entwicklungs- und Qualitätssicherungsaufwand verringert. Wenn alles, was getestet wird, die Öffentlichkeit ist, haben Sie keine ehrliche Berichterstattung und Abgrenzung der Interna der Klasse.

Ich habe eine der besten Möglichkeiten gefunden, dies zu tun, indem ich einfach den Testverweis zum Projekt hinzufüge und die Tests in eine Klasse parallel zu den privaten Methoden einfüge. Geben Sie die entsprechende Build-Logik ein, damit die Tests nicht in das endgültige Projekt integriert werden.

Dann haben Sie alle Vorteile, diese Methoden testen zu lassen, und Sie können Probleme in Sekunden gegenüber Minuten oder Stunden finden.

Also zusammenfassend, ja, Unit-Test Ihrer privaten Methoden.


2
Ich stimme dir nicht zu. "Wenn Sie Ihre privaten Methoden nicht testen, woher wissen Sie, dass sie nicht kaputt gehen?" : Ich weiß das, denn wenn meine privaten Methoden fehlerhaft sind, schlagen die Tests fehl, die meine öffentlichen Methoden testen, die auf diesen privaten Methoden basieren. Ich möchte meine Tests nicht jedes Mal ändern müssen, wenn ich meine Meinung über die Implementierung der öffentlichen Methoden ändere. Ich denke auch, dass das Hauptinteresse von Unit-Tests nicht darin besteht, genau zu wissen, welche Codezeile fehlerhaft ist, sondern dass Sie mehr oder weniger sicher sein können, dass Sie bei Änderungen (an den privaten Methoden) nichts kaputt gemacht haben.
Tesseract

6

Das solltest du nicht . Wenn Ihre privaten Methoden eine ausreichende Komplexität aufweisen, die getestet werden muss, sollten Sie sie einer anderen Klasse zuordnen. Behalten Sie einen hohen Zusammenhalt bei , eine Klasse sollte nur einen Zweck haben. Die öffentliche Schnittstelle der Klasse sollte ausreichen.


3

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


19
Durch Schreiben durch Tests Ihrer öffentlichen Methoden.
Scubabbl

3
Diese privaten Methoden werden angeblich von den öffentlichen Methoden der Klasse aufgerufen. Testen Sie also einfach die öffentlichen Methoden, die die privaten Methoden aufrufen.
Jop

1
Wenn Ihre öffentlichen Methoden ordnungsgemäß funktionieren, funktionieren die privaten Methoden, auf die sie zugreifen, offensichtlich ordnungsgemäß.
17 vom 26.

Wenn die Tests Ihrer öffentlichen Methoden fehlschlagen, wissen Sie sofort, dass auf einer niedrigeren Ebene in Ihrem Objekt / Ihrer Komponente / etc. Etwas nicht stimmt.
Rob

3
Es ist jedoch sehr schön zu wissen, dass es sich um eine interne Funktion handelt und nicht nur um die externen Funktionen, die kaputt gegangen sind (oder umgekehrt, dass die inneren Funktionen in Ordnung sind und Sie sich auf die externen konzentrieren können).
Adam Davis

2

Es ist offensichtlich sprachabhängig. In der Vergangenheit habe ich mit c ++ die Testklasse als Freundklasse deklariert. Leider erfordert dies, dass Ihr Produktionscode über die Testklasse Bescheid weiß.


5
Das Schlüsselwort friend macht mich traurig.
Rob

Dies ist kein Problem, wenn die Testklasse in einem anderen Projekt implementiert ist. Wichtig ist, dass der Produktionscode nicht auf die Testklasse verweist.
Olumide

2

Ich verstehe den Standpunkt, dass private Methoden als Implementierungsdetails betrachtet werden und dann nicht getestet werden müssen. Und ich würde mich an diese Regel halten, wenn wir uns nur außerhalb des Objekts entwickeln müssten. Aber sind wir eine Art eingeschränkter Entwickler, die sich nur außerhalb von Objekten entwickeln und nur ihre öffentlichen Methoden aufrufen? Oder entwickeln wir dieses Objekt tatsächlich auch? Da wir nicht verpflichtet sind, externe Objekte zu programmieren, müssen wir diese privaten Methoden wahrscheinlich in neue öffentliche Methoden aufrufen, die wir entwickeln. Wäre es nicht toll zu wissen, dass die private Methode allen Widrigkeiten widersteht?

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

Warum testen wir Objekte? Weil es wirklich schwierig ist, nicht unmöglich zu sagen, 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 Stapel 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. Kriterien, die mir wichtiger sind, sind:

  • Wird die Methode mehrmals von verschiedenen Orten aus aufgerufen?
  • Ist die Methode so ausgefeilt, dass Tests erforderlich sind?

1

Wenn ich feststelle, dass die private Methode riesig oder komplex oder wichtig genug ist, um eigene Tests zu erfordern, stelle ich sie einfach in eine andere Klasse und mache sie dort öffentlich (Methodenobjekt). Dann kann ich leicht die zuvor private, aber jetzt öffentliche Methode testen, die jetzt in ihrer eigenen Klasse lebt.


1

Ich verstehe das Konzept des Unit Test nie, aber jetzt weiß ich, was das Ziel ist.

Ein Unit Test ist kein vollständiger Test . Es ist also kein Ersatz für QS 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, auf die wir keinen Einfluss haben). TDD basiert auf all seiner Qualität, die nicht erreicht werden konnte.

Ein Unit-Test ist eher ein Pivot-Test. Sie markieren einen beliebigen Pivot und das Ergebnis des Pivots sollte gleich bleiben.


1

Öffentlich oder privat ist weder eine nützliche Unterscheidung für das, was apis aus Ihren Tests aufruft, noch Methode gegen Klasse. Die meisten testbaren Einheiten sind in einem Kontext sichtbar, in anderen jedoch verborgen.

Was zählt, ist Deckung und Kosten. Sie müssen die Kosten minimieren und gleichzeitig die Abdeckungsziele Ihres Projekts erreichen (Linie, Zweig, Pfad, Block, Methode, Klasse, Äquivalenzklasse, Anwendungsfall ... was auch immer das Team entscheidet).

Verwenden Sie daher Tools, um die Abdeckung sicherzustellen, und gestalten Sie Ihre Tests so, dass sie die geringsten Kosten verursachen (kurz- und langfristig ).

Machen Sie Tests nicht teurer als nötig. Wenn es am billigsten ist, nur öffentliche Einstiegspunkte zu testen, tun Sie dies. Wenn es am billigsten ist, private Methoden zu testen, tun Sie das.

Je erfahrener Sie werden, desto besser können Sie vorhersagen, wann es sich lohnt, eine Umgestaltung vorzunehmen, um langfristige Kosten für die Testwartung zu vermeiden.


0

Wenn die Methode signifikant genug / komplex genug ist, mache ich sie normalerweise "geschützt" und teste sie. Einige Methoden werden privat gelassen und implizit im Rahmen von Unit-Tests für die öffentlichen / geschützten Methoden getestet.


1
@VisibleForTesting ist eine Anmerkung dafür. Ich würde die Kapselung nicht zum Testen entspannen, sondern dp4j.com
simpatico

0

Ich sehe, dass viele Menschen in der gleichen Denkweise sind: Test auf öffentlicher Ebene. Aber ist es nicht das, was unser QA-Team tut? Sie testen die Eingabe und die erwartete Ausgabe. Wenn wir als Entwickler nur die öffentlichen Methoden testen, wiederholen wir einfach den Job von QA und fügen durch "Unit-Tests" keinen Mehrwert hinzu.


Der aktuelle Trend geht dahin, das QS-Team zu reduzieren oder nicht zu haben. Diese Komponententests werden zu automatisierten Tests, die jedes Mal ausgeführt werden, wenn ein Techniker den Code auf den Hauptzweig drückt. Selbst mit der Qualitätssicherung können sie die gesamte Anwendung nicht so schnell testen wie automatisierte Tests.
Patrick Desjardins

0

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

  • Einer der Gründe ist, dass Sie für eine Funktion keine doppelte Abdeckung benötigen.
  • Ein weiterer Grund ist, dass Sie, wenn Sie private Methoden ändern, 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 RefactoredThingSie haben nun 5 Tests, von denen 2 Sie Refactoring aktualisieren musste, aber das Objekt Funktionalität wirklich nicht geändert hat. Nehmen wir also an, die Dinge sind komplexer als das 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 Kapselungsklasse ist möglicherweise zu schwer, um so viel Logik immer wieder auszuführen. In diesem Fall möchten Sie dies vielleicht lieber in eine separate Klasse extrahieren, dieser Klasse eine Schnittstelle geben und dagegen testen.

Nehmen wir zum Schluss an, Ihr Hauptobjekt ist sehr schwer, die Methode ist recht klein und Sie müssen wirklich sicherstellen, dass die Ausgabe korrekt ist. Sie denken: "Ich muss diese private Methode testen!". Haben Sie das, dass Sie Ihr Objekt vielleicht leichter machen können, indem Sie einen Teil der schweren Arbeit als Initialisierungsparameter übergeben? Dann können Sie etwas Leichteres hineingeben und dagegen testen.


0

Nein Sie sollten die privaten Methoden nicht testen, warum? Darüber hinaus bietet das beliebte Mocking-Framework wie Mockito keine Unterstützung für das Testen privater Methoden.


0

Ein Hauptpunkt ist

Wenn wir testen, um die Richtigkeit der Logik sicherzustellen, und eine private Methode eine Logik enthält, sollten wir sie testen. Ist es nicht? Warum überspringen wir das?

Das Schreiben von Tests, die auf der Sichtbarkeit von Methoden basieren, ist völlig irrelevant.

Umgekehrt

Andererseits ist das Aufrufen einer privaten Methode außerhalb der ursprünglichen Klasse ein Hauptproblem. Außerdem gibt es in einigen Verspottungswerkzeugen Einschränkungen, eine private Methode zu verspotten. (Beispiel: Mockito )

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

Eine Möglichkeit, dies zu umgehen, ist (wenn Sie Testfälle für private Methoden schreiben möchten)

Deklarieren Sie diese privaten Methoden als geschützt . In einigen Situationen ist dies jedoch möglicherweise nicht bequem.


0

Es geht nicht nur um öffentliche oder private Methoden oder Funktionen, sondern auch um Implementierungsdetails. Private Funktionen sind nur ein Aspekt der Implementierungsdetails.

Unit-Tests sind schließlich ein White-Box-Testansatz. Wer beispielsweise die Abdeckungsanalyse verwendet, um Teile des Codes zu identifizieren, die bisher beim Testen vernachlässigt wurden, geht auf die Implementierungsdetails ein.

A) Ja, Sie sollten die Implementierungsdetails testen:

Stellen Sie sich eine Sortierfunktion vor, die aus Leistungsgründen eine private Implementierung von BubbleSort verwendet, wenn bis zu 10 Elemente vorhanden sind, 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 sicherlich für die öffentliche 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 ist. Das Vorhandensein solcher Testfälle allein ist ein Hinweis darauf, dass die Testsuite mit den Implementierungsdetails der Funktion verbunden ist.

Wenn sich die Implementierungsdetails der Sortierfunktion ändern, möglicherweise in der Weise, dass die Grenze zwischen den beiden Sortieralgorithmen verschoben wird oder Heapsort durch Mergesort oder was auch immer ersetzt wird: Die vorhandenen Tests funktionieren weiterhin. Ihr Wert ist dann jedoch fraglich, und sie müssen wahrscheinlich überarbeitet werden, um die geänderte Sortierfunktion besser testen zu können. Mit anderen Worten, es wird einen Wartungsaufwand geben, obwohl Tests auf der öffentlichen API durchgeführt wurden.

B) Testen der Implementierungsdetails

Ein Grund, warum viele Leute argumentieren, man sollte private Funktionen oder Implementierungsdetails nicht testen, ist, dass sich die Implementierungsdetails eher ändern. Diese höhere Wahrscheinlichkeit von Änderungen ist zumindest einer der Gründe dafür, Implementierungsdetails hinter Schnittstellen zu verbergen.

Angenommen, die Implementierung hinter der Schnittstelle enthält größere private Teile, für die einzelne Tests auf der internen Schnittstelle eine Option sein könnten. Einige Leute argumentieren, diese Teile sollten nicht getestet werden, wenn sie privat sind, sie sollten in etwas Öffentliches verwandelt werden. Einmal öffentlich, wäre es in Ordnung, diesen Code zu testen.

Dies ist interessant: Während die Schnittstelle intern war, wurde sie wahrscheinlich geändert, da es sich um ein Implementierungsdetail handelt. Wenn Sie dieselbe Schnittstelle verwenden und sie öffentlich machen, wird eine magische Transformation durchgeführt, nämlich die Umwandlung in eine Schnittstelle, die sich weniger wahrscheinlich ändert. Offensichtlich gibt es einen Fehler in dieser Argumentation.

Dahinter steckt jedoch eine gewisse Wahrheit: 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 ist, kann jedoch nicht einfach anhand der öffentlichen oder privaten Schnittstelle entschieden werden. In den Projekten aus der Welt, in denen ich seit einiger Zeit arbeite, ändern sich auch die öffentlichen Schnittstellen oft genug, und viele private Schnittstellen sind seit Ewigkeiten unberührt geblieben.

Dennoch ist es eine gute Faustregel, die "Haustür zuerst" zu verwenden (siehe http://xunitpatterns.com/Principles%20of%20Test%20Automation.html) ). Aber denken Sie daran, dass es "Haustür zuerst" und nicht "Haustür nur" 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 Tests der öffentlichen API überarbeitet werden. Etwas Privates in öffentliches zu verwandeln, verändert seine Stabilität nicht auf magische Weise.


0

Ja, Sie sollten nach Möglichkeit private Methoden testen. Warum? Um eine unnötige Explosion des Zustandsraums zu vermeiden von Testfällen im die letztendlich implizit dieselben privaten Funktionen wiederholt an denselben Eingaben testen. Lassen Sie uns anhand eines Beispiels erklären, warum.

Betrachten Sie das folgende leicht erfundene Beispiel. Angenommen, wir möchten eine Funktion öffentlich 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 würden, dass nur öffentliche Funktionen getestet werden sollten, dürfen wir nur testen allPrimeund nichtisPrime oder andAll.

Als Tester, könnten wir in fünf Möglichkeiten für jedes Argument interessiert sein: < 0, = 0, = 1, prime > 1,not prime > 1 . Aber um gründlich zu sein, müssten wir auch sehen, wie jede Kombination der Argumente zusammenspielt. So das ist5*5*5 = 125 Testfälle, die wir gemäß unserer Intuition gründlich testen müssten.

Wenn wir andererseits 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 isPrimeauf dem gleichen Niveau wie unsere vorherige Intuition zu testen . Und nach der von Daniel Jackson vorgeschlagenen Small-Scope-Hypothese müssten wir die andAllFunktion nur bis zu einer kleinen Länge testen, z. B. 3 oder 4. Das wären höchstens 16 weitere Tests. Also insgesamt 21 Tests. Anstelle von 125. Natürlich würden wir wahrscheinlich ein paar Tests durchführen wollen allPrime, aber wir würden uns nicht so verpflichtet fühlen, alle 125 Kombinationen von Eingabeszenarien, von denen wir sagten, dass sie uns wichtig sind, ausführlich abzudecken. Nur ein paar glückliche Wege.

Sicher ein erfundenes Beispiel, aber es war für eine klare Demonstration notwendig. Und das Muster erstreckt sich auf echte Software. Private Funktionen sind normalerweise die Bausteine ​​der untersten Ebene und werden daher häufig miteinander kombiniert, um eine Logik auf höherer Ebene zu erhalten. Das heißt, auf höheren Ebenen haben wir aufgrund der verschiedenen Kombinationen mehr Wiederholungen der Sachen auf niedrigeren Ebenen.


Erstens müssen Sie solche Kombinationen nicht mit reinen Funktionen testen, wie Sie gezeigt haben. Anrufe isPrimesind wirklich unabhängig, daher ist es ziemlich zwecklos, jede Kombination blind zu testen. Zweitens isPrimeverstößt das Markieren einer reinen Funktion namens privat gegen so viele Entwurfsregeln, dass ich nicht einmal weiß, wo ich anfangen soll. isPrimesollte ganz klar eine öffentliche Funktion sein. Davon abgesehen verstehe ich, was Sie sagen, ungeachtet dieses extrem schlechten Beispiels. Es basiert jedoch auf der Voraussetzung, dass Sie Kombinationstests durchführen möchten , wenn dies in realen Softwaresystemen selten eine gute Idee ist.
Matt Messersmith

Matt ja das Beispiel ist nicht ideal das gebe ich dir. Das Prinzip sollte jedoch offensichtlich sein.
Colm Bhandal

Die Voraussetzung ist nicht genau, dass Sie Kombinationstests durchführen möchten. Es ist so, dass Sie es müssten, wenn Sie sich darauf beschränken würden, nur öffentliche Puzzleteile zu testen. Es gibt Fälle, in denen Sie eine reine Funktion privat machen möchten, um die richtigen Kapselungsprinzipien einzuhalten. Und diese rein private Funktion könnte von öffentlichen genutzt werden. In kombinatorischer Weise, vielleicht mit anderen rein privaten Funktionen. In diesem Fall müssten Sie nach dem Dogma, dass Sie nicht privat testen sollen, Kombinationstests für die öffentliche Funktion durchführen, anstatt die privaten Komponenten modular zu testen.
Colm Bhandal

0

Sie können Ihre Methode auch als privat festlegen, dh als Standard, und Sie sollten in der Lage sein, sie einem Komponententest zu unterziehen, es sei denn, sie muss privat sein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.