Warum werden Unit-Tests für private Methoden als schlechte Praxis angesehen?

15

Kontext:

Ich arbeite derzeit an einem kleinen Projekt in Python. Normalerweise strukturiere ich meine Klassen mit einigen öffentlichen Methoden, die dokumentiert sind, sich aber hauptsächlich mit den Konzepten auf hoher Ebene (was ein Benutzer der Klasse wissen und verwenden sollte) und einer Reihe versteckter (beginnend mit Unterstrich) Methoden befassen, die für die zuständig sind komplexe oder Low-Level-Verarbeitung.

Ich weiß, dass Tests wichtig sind, um Vertrauen in den Code zu schaffen und um sicherzustellen, dass spätere Änderungen das vorherige Verhalten nicht verletzt haben.

Problem:

Um die übergeordneten öffentlichen Methoden auf einer vertrauenswürdigen Basis aufzubauen, teste ich im Allgemeinen die privaten Methoden. Ich finde es einfacher zu finden, ob und wo eine Codeänderung Regressionen eingeführt hat. Dies bedeutet, dass diese internen Tests bei geringfügigen Änderungen brechen können und repariert / ersetzt werden müssen

Ich weiß aber auch, dass Unit-Testing-Private-Methoden zumindest ein umstrittenes Konzept sind oder häufiger als schlechte Praxis angesehen werden. Der Grund ist: Nur öffentliches Verhalten sollte getestet werden ( Ref. )

Frage:

Ich kümmere mich um die folgenden Best Practices und möchte verstehen:

  • Warum ist die Verwendung von Unit-Tests für private / versteckte Methoden schlecht (was ist das Risiko)?
  • Was sind die Best Practices, wenn die öffentlichen Methoden eine einfache und / oder komplexe Verarbeitung verwenden können?

Präzision:

  • es ist nicht ein , wie man Frage. Python hat kein echtes Konzept der Privatsphäre und versteckte Methoden werden einfach nicht aufgelistet, können aber verwendet werden, wenn Sie ihren Namen kennen
  • Ich habe noch nie Programmierregeln und -muster gelernt: Meine letzten Kurse stammen aus den 80ern ... Ich habe hauptsächlich Sprachen durch Ausprobieren und Referenzen im Internet gelernt (Stack Exchange ist seit Jahren mein Favorit).
Serge Ballesta
quelle
2
Mögliches Duplikat des Testens privater Methoden als geschützt
Greg Burghardt
2
OP, wo haben Sie gehört oder gelesen, dass das Testen privater Methoden als "schlecht" eingestuft wurde? Es gibt verschiedene Möglichkeiten zum Unit-Test. Siehe Unit-Tests, Black-Box-Tests und White-Box-Tests .
John Wu
@ JohnWu: Ich kenne den Unterschied zwischen White-Box- und Black-Box-Tests. Aber selbst beim White-Box-Testen scheint die Notwendigkeit, private Methoden zu testen, ein Hinweis auf ein Designproblem zu sein. Meine Frage ist ein Versuch zu verstehen, was die besten Wege sind, wenn ich dort falle ...
Serge Ballesta
2
Wo haben Sie gehört oder gelesen, dass selbst beim White-Box-Testen die Notwendigkeit, private Methoden zu testen, ein Hinweis auf ein Designproblem ist? Ich möchte die Gründe für diesen Glauben verstehen, bevor ich eine Antwort versuche.
John Wu
@SergeBallesta mit anderen Worten, geben Sie einige Verweise auf Artikel an, die Sie glauben gemacht haben, dass das Testen privater Methoden eine schlechte Praxis ist. Dann erklären Sie uns, warum Sie ihnen geglaubt haben.
Laiv

Antworten:

17

Ein paar Gründe:

  1. Wenn Sie versucht sind, die private Methode einer Klasse zu testen, ist dies normalerweise ein Designgeruch (Eisbergklasse, nicht genügend wiederverwendbare öffentliche Komponenten usw.). Es gibt fast immer ein "größeres" Problem.

  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!

Ein Tipp, wenn Sie immer noch versucht sind, die privaten Teile zu testen (verwenden Sie es nicht, wenn es Sie und YMMV stört, aber es hat in der Vergangenheit für mich gut funktioniert ): Manchmal schreiben Sie Unit-Tests für private Funktionen, nur um sicherzugehen Sie arbeiten genau so, wie Sie denken, dass sie wertvoll sein können (insbesondere, wenn Sie neu in einer Sprache sind). Wenn Sie jedoch sicher sind, dass sie funktionieren, löschen Sie die Tests und stellen Sie immer sicher, dass die öffentlich zugänglichen Tests solide sind und abfangen, wenn jemand eine ungeheure Änderung an dieser privaten Funktion vornimmt.

Wann man private Methoden testet: Da diese Antwort (etwas) populär geworden ist, fühle ich mich verpflichtet zu erwähnen, dass eine "Best Practice" immer genau das ist: eine "Best Practice". Es bedeutet nicht, dass Sie es dogmatisch oder blind tun sollten. Wenn Sie der Meinung sind, dass Sie Ihre privaten Methoden testen sollten und einen legitimen Grund haben (z. B. wenn Sie Charakterisierungstests für eine Legacy-Anwendung schreiben), testen Sie Ihre privaten Methoden . Bestimmte Umstände sind immer wichtiger als allgemeine Regeln oder bewährte Verfahren. Seien Sie sich nur einiger Dinge bewusst, die schief gehen können (siehe oben).

Ich habe eine Antwort, die dies auf SO ausführlich behandelt und die ich hier nicht wiederholen werde: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015

Matt Messersmith
quelle
4
Grund 1: Nebulös. Grund 2: Was ist, wenn Ihre private Hilfsmethode nicht Teil der öffentlichen API sein sollte? Grund 3: Nicht, wenn Sie Ihre Klasse richtig gestalten. Ihr letzter Tipp: Warum sollte ich einen einwandfreien Test löschen, der beweist, dass eine von mir geschriebene Methode funktioniert?
Robert Harvey
4
@RobertHarvey Grund 2: Indirekter Zugriff über die öffentliche API! = Teil der öffentlichen API sein. Wenn Ihre private Funktion nicht über die öffentliche API getestet werden kann, handelt es sich möglicherweise um einen toten Code, der entfernt werden sollte. Oder Ihre Klasse ist in der Tat ein Eisberg (Grund 1) und sollte überarbeitet werden.
Frax
5
@RobertHarvey Wenn Sie eine private Funktion nicht über eine öffentliche API testen können, löschen Sie sie, da sie keinen nützlichen Zweck erfüllt.
David Arno
1
@ RobertHarvey 1: Designgerüche sind immer etwas subjektiv / nebulös, also sicher. Aber ich habe einige konkrete Beispiele für Anti-Muster aufgelistet, und meine SO-Antwort enthält mehr Details. 2: Private Methoden können nicht Teil der öffentlichen API sein (per Definition: sie sind privat) ... daher halte ich Ihre Frage nicht für sinnvoll. Ich versuche zu dem Punkt zu gelangen, dass, wenn Sie so etwas wie bin_search(arr, item)(öffentlich) und bin_search(arr, item, low, hi)(privat, es gibt viele Möglichkeiten, eine Bin-Suche durchzuführen), alles, was Sie testen müssen, das öffentlich zugängliche ist ( bin_search(arr, item))
Matt Messersmith,
1
@ RobertHarvey 3: Erstens sagte ich Risiko , nicht Garantie . Zweitens ist die Behauptung "es funktioniert, wenn Sie es richtig machen" selbst erfüllend. Zum Beispiel "Sie können ein Betriebssystem in einer einzigen Funktion schreiben, wenn Sie es richtig machen ". Das ist nicht falsch, aber es ist auch nicht nützlich. Über den Tipp: Sie würden es löschen, denn wenn sich Ihre Implementierung ändert (dh Sie möchten ein privates Gerät austauschen), wird Ihre Testsuite Ihnen im Weg stehen (Sie haben einen fehlgeschlagenen Test, bei dem Sie dies nicht tun sollten).
Matt Messersmith
13

Angesichts der Tatsache, dass einer der Hauptzwecke von Komponententests darin besteht, dass Sie die Interna Ihres Programms umgestalten und dann überprüfen können, ob Sie seine Funktionalität nicht beeinträchtigt haben, ist es kontraproduktiv, wenn Ihre Komponententests mit einer so feinen Granularität ausgeführt werden, dass Bei jeder Änderung des Programmcodes müssen Sie Ihre Tests neu schreiben.

Pete
quelle
Ich bin mir nicht sicher, warum Ihre Antwort abgelehnt wurde. Es ist kurz, auf den Punkt und bekommt die Antwort 100% richtig.
David Arno
3
@DavidArno: Vielleicht, weil das Testen privater Methoden nicht wirklich viel mit der Testgranularität zu tun hat. Es hat alles mit der Kopplung an Implementierungsdetails zu tun.
Robert Harvey
10

Das Schreiben von Komponententests für private Methoden verknüpft Ihre Komponententests mit Implementierungsdetails.

Unit-Tests sollten das Verhalten einer Klasse an der Außenfläche der Klasse testen (öffentliche API). Unit-Tests sollten nichts über die Innereien einer Klasse wissen müssen. Das Schreiben von Komponententests anhand der Implementierungsdetails einer Klasse bindet Ihnen die Hände, wenn es um die Umgestaltung geht. Refactoring wird diese Tests mit ziemlicher Sicherheit brechen, da sie nicht Teil Ihrer stabilen API sind.

Das heißt, warum möchten Sie vielleicht Unit-Tests für Ihre privaten Methoden schreiben?

Es gibt eine natürliche Spannung zwischen Unit-Tests und inkrementeller Entwicklung. Softwareentwickler, die eine REPL (Read-Eval-Print-Schleife) verwenden, können bestätigen, wie produktiv es sein kann, kleine Funktionen schnell zu schreiben und zu testen, wenn Sie eine Klasse oder Funktion "erweitern". Der einzige gute Weg, dies in Umgebungen zu tun, die auf Unit-Tests basieren, besteht darin, Unit-Tests für private Methoden zu schreiben, aber das ist sehr reibungslos. Das Schreiben von Komponententests benötigt Zeit, Sie benötigen eine tatsächliche Methode zum Testen, und Ihr Testframework muss die Fähigkeit unterstützen, die Methode privat zu halten, damit Ihre externe API nicht verschmutzt wird.

Einige Ökosysteme wie C # und .NET bieten Möglichkeiten zum Erstellen von REPL-ähnlichen Umgebungen (Tools wie Linqpad tun dies), ihre Nützlichkeit ist jedoch eingeschränkt, da Sie keinen Zugriff auf Ihr Projekt haben. Das unmittelbare Fenster in Visual Studio ist unpraktisch. Es verfügt immer noch nicht über volles Intellisense, Sie müssen vollständig qualifizierte Namen verwenden und es löst jedes Mal einen Build aus, wenn Sie es verwenden.

Robert Harvey
quelle
4
@ user949300 Es ist ein bisschen schwierig, dies zu diskutieren, ohne in den nicht wahren schottischen Irrtum zu verfallen , aber es gibt viele schlecht geschriebene Tests aller Art. Aus Sicht der Unit-Tests sollten Sie den öffentlichen Auftrag Ihrer Methode testen, ohne die Details der internen Implementierung zu kennen. Nicht dass die Behauptung, dass eine bestimmte Abhängigkeit X-mal aufgerufen wurde, immer falsch ist: Es gibt Situationen, in denen dies sinnvoll ist. Sie müssen nur sicherstellen, dass dies eine Information ist, die Sie tatsächlich im Vertrag des zu testenden Geräts übermitteln möchten.
Vincent Savard
3
@ DavidArno: [Achselzucken] Ich mache das jetzt schon eine Weile. Unit-Tests für private Methoden funktionierten für mich immer einwandfrei, bis Microsoft beschloss, die Unterstützung von Proxy-Objekten in ihrem Test-Framework einzustellen. Infolgedessen explodierte nie etwas. Ich habe nie ein Loch in das Universum gerissen, indem ich einen Test für eine private Methode geschrieben habe.
Robert Harvey
2
@DavidArno: Warum sollte ich aufhören, eine vollkommen gute Technik zu verwenden, die mir Vorteile bringt, nur weil jemand im Internet sagt, es sei eine schlechte Idee, ohne eine Begründung zu liefern?
Robert Harvey
2
Der Hauptvorteil von Unit-Tests besteht darin, mir ein "Sicherheitsnetz" zu geben, mit dem ich an meinem Code basteln und sicher sein kann, dass meine Änderungen keine Regressionen hervorrufen. Zu diesem Zweck erleichtert das Testen privater Hilfsmethoden das Auffinden solcher Regressionen. Wenn ich eine private Hilfsmethode umgestalte und einen logischen Fehler einführe, unterbreche ich Tests, die für diese private Methode spezifisch sind. Wenn meine Komponententests allgemeiner wären und nur die Schnittstelle dieser Codeeinheit testen würden, wäre das Problem viel dunkler zu finden.
Alexander - Reinstate Monica
2
@Frax Sicher, ich könnte, aber nach dieser Logik sollte ich auf Unit-Tests zugunsten systemweiter Integrationstests verzichten. Schließlich "sollten Sie in den meisten Fällen in der Lage sein, diese Tests zu ändern, um dasselbe Verhalten zu testen"
Alexander - Reinstate Monica
6

Aus meiner Erfahrung habe ich herausgefunden, dass Unit-Tests der internen Klassen, Methoden normalerweise bedeuten, dass ich die getesteten Funktionen, Klassen herausnehmen muss. Eine andere Abstraktionsebene erstellen.

Dies führt zu einer besseren Einhaltung des Einzelverantwortungsprinzips.

Robert Andrzejuk
quelle
Dies sollte die akzeptierte Antwort sein.
Jack Aidley
0

Ich denke, dies ist eine gute Frage, da sie ein häufiges Problem beim Testen der Abdeckung aufzeigt. Eine gute Antwort sollte Ihnen jedoch sagen, dass die Frage nicht genau richtig ist, da Sie theoretisch nicht in der Lage sein sollten, private Methoden zu testen . Deshalb sind sie privat .

Vielleicht wäre eine bessere Frage: "Was soll ich tun, wenn ich private Methoden testen möchte?" und die Antwort liegt auf der Hand: Sie sollten sie so belichten, dass Tests möglich sind. Dies bedeutet nicht unbedingt, dass Sie die Methode nur öffentlich machen sollten, und das ist es. Höchstwahrscheinlich möchten Sie eine höhere Abstraktion durchführen. Wechseln Sie zu einer anderen Bibliothek oder API, damit Sie Ihre Tests für diese Bibliothek durchführen können, ohne diese Funktionalität in Ihrer Haupt-API verfügbar zu machen.

Denken Sie daran, dass es einen Grund gibt, warum Ihre Methoden unterschiedliche Zugänglichkeitsstufen haben, und Sie sollten immer darüber nachdenken, wie Ihre Klassen am Ende verwendet werden.

Juan Carlos Eduardo Romaina Ac
quelle
Ich habe versucht, dies in meiner Frage zu beantworten, indem ich sagte, dass Python kein echtes Konzept der Privatsphäre hat und versteckte Methoden einfach nicht aufgelistet sind, sondern verwendet werden können, wenn Sie ihren Namen kennen
Serge Ballesta