wake-up-neo.com

junit & Java: Testen nicht öffentlicher Methoden

JUnit testet in meiner Klasse nur die Methoden, die öffentlich sind. Wie mache ich Junit-Tests für diejenigen, die nicht (also privat, geschützt) sind? 

Ich kann sie testen, indem ich junit nicht verwende, aber ich habe mich gefragt, was die Junit-Standardmethode ist.

91
jbu

Eine Denkrichtung über Komponententests besagt, dass Sie nur öffentliche Methoden testen dürfen, da Sie nur Ihre öffentliche API als Komponententest testen sollten. Dadurch sollten Sie den Code in Ihren nicht öffentlichen Methoden behandeln. Ihre Laufleistung kann variieren. Ich finde, dass dies manchmal der Fall ist und manchmal nicht.

Es gibt jedoch einige Möglichkeiten, nichtöffentliche Methoden zu testen:

  • Sie können geschützte Methoden und den Umfang des Paketbereichs testen, indem Sie Ihre Komponententests im selben Paket wie die von ihnen getesteten Klassen ablegen. Dies ist eine ziemlich übliche Praxis.
  • Sie können geschützte Methoden aus Komponententests in einem anderen Paket testen, indem Sie eine Unterklasse der zu testenden Klasse erstellen, die die zu testenden Methoden überschreibt, und die überschriebenen Methoden die ursprünglichen Methoden mit dem Schlüsselwort super aufrufen. Normalerweise handelt es sich bei dieser "Testunterklasse" um eine innere Klasse in der JUnit TestCase-Klasse, die den Test durchführt. Das ist meiner Meinung nach etwas hackiger, aber ich habe es geschafft.

Hoffe das hilft.

86
MattK

Wie bei vielen Unit-Test-Problemen ist das Testen privater Methoden tatsächlich ein verdecktes Konstruktionsproblem. Anstatt zu versuchen, etwas kompliziertes zu testen, um private Methoden zu testen, frage ich mich, wenn ich Tests für private Methoden schreiben möchte: "Wie muss ich das entwerfen, damit ich es gründlich mit öffentlichen Methoden testen kann?"

Wenn dies nicht funktioniert, erlaubt JUnitX das Testen privater Methoden, obwohl ich glaube, dass es nur für JUnit 3.8 verfügbar ist.

36
Kent Beck

Wenn Sie einen JUnit-Test schreiben, müssen Sie eine subtile Denkänderung vornehmen: "Ich bin jetzt ein Kunde meiner eigenen Klasse." Dies bedeutet, dass privat privat ist und Sie nur das Verhalten testen, das der Client sieht. 

Wenn die Methode wirklich privat sein sollte, würde ich sie als Konstruktionsfehler betrachten, um sie nur für Tests sichtbar zu machen. Sie müssen in der Lage sein, die korrekte Operation basierend auf dem, was der Client sieht, zu schließen.

In den drei Jahren, seitdem ich das geschrieben habe, habe ich angefangen, das Problem etwas anders anzugehen, indem ich Java-Reflektion verwendete.

Das schmutzige kleine Geheimnis ist, dass Sie private Methoden in JUnit genauso wie öffentliche Methoden mithilfe von Reflection testen können. Sie können nach Herzenslust testen und sie dennoch nicht als öffentlich für Kunden verfügbar machen.

25
duffymo

Die einfachste Lösung besteht darin, die JUnit-Tests in demselben Paket (aber in einem anderen Verzeichnis) abzulegen und die standardmäßige Sichtbarkeit (d. H. Package-private) für die Methoden zu verwenden.

Ein weiterer komplizierterer Ansatz ist die Verwendung der Reflektion, um auf private Methoden zuzugreifen.

9
starblue

Wenn Sie eine beträchtliche Menge Logik unter relativ wenigen "öffentlichen" Eintrittspunkten vergraben haben, verstoßen Sie wahrscheinlich gegen das Prinzip der Einzelverantwortung . Wenn möglich, sollten Sie den Code in mehrere Klassen umwandeln, was letztendlich zu mehr "öffentlichen" Methoden führt, von denen aus getestet werden kann.

9
marcumka

Hier ist die "wahrscheinlich nicht so" -Methode, bei der alle anderen ständig auf Sie einreden. Ich denke, es liegt durchaus im Bereich der Möglichkeit, dass es Gründe gibt, dies auf diese Weise zu tun. Der folgende Code greift auf ein privates Feld zu, der Code für eine private Methode ist jedoch nahezu identisch.

public void testPrivateField() throws InterruptedException {
    Class<ClassWPrivateField> clazz = ClassWPrivateField.class;
    try {
        Field privateField = clazz.getDeclaredField("nameOfPrivateField");
        privateField.setAccessible(true); // This is the line
        // do stuff
    } catch(NoSuchFieldException nsfe) {
        nsfe.printStackTrace();
        fail();
    } catch(IllegalAccessException iae) {
        iae.printStackTrace();
        fail();
    }

}
8
Cheese Daneish

Ich verwende Spring fast immer in meinen Java-Projekten und daher sind meine Objekte für die Abhängigkeitsinjektion gebaut. Sie sind in der Regel ziemlich granulare Implementierungen öffentlicher Schnittstellen, die im Anwendungskontext zusammengestellt werden. Als solches habe ich selten (wenn überhaupt) die Notwendigkeit, private Methoden zu testen, da die Klasse selbst klein genug ist, dass sie einfach kein Problem darstellt.

Selbst wenn ich Spring nicht verwende, neige ich dazu, die gleichen Praktiken anzuwenden, kleine und einfache Objekte zu immer größeren Abstraktionen zusammenzufügen, von denen jede relativ einfach ist, aber durch die aggregierten Objekte komplex wird.

Nach meiner Erfahrung ist die Notwendigkeit, einzelne Methoden zu testen, ein Indikator dafür, dass das, was Ihnen Spaß macht, vereinfacht werden könnte (und sollte).

Das heißt, wenn Sie immer noch das Bedürfnis verspüren:

  • Geschützte Methoden können von Unterklassen getestet werden.
  • Private Methoden des Pakets können getestet werden, indem die Komponententests im selben Paket abgelegt werden. und
  • Private Methoden können Unit-Tests unterzogen werden, indem beispielsweise eine private Factory-Proxy-Methode des Pakets bereitgestellt wird. Nicht ideal, aber privat bedeutet privat.
3
cletus

Sie testen private Methoden normalerweise nicht, da sie nur indirekt über eine andere öffentliche Methode getestet werden können. Wenn Sie Probefahren machen und private Methoden erstellen, sind sie normalerweise das Ergebnis einer Refactoring-Methode für "Extraktionsmethode" und werden bereits indirekt getestet.

Wenn Sie über das Testen einer privaten Methode mit viel Logik besorgt sind, sollten Sie den Code in einer öffentlichen Methode in eine andere Klasse verschieben. Sobald Sie dies getan haben, kann der Test der vorherigen Methode, die diesen Code verwendet, vereinfacht werden, indem die Funktionalität durch einen Stub oder einen Mock bereitgestellt wird.

3
Spoike

DP4j Jar

Um private Methoden zu testen, müssen wir die Reflexion verwenden und in allen Antworten zeigen.

nun, diese Aufgabe wird nun mit Dp4j jar vereinfacht.

  • Dp4j analysiert Ihren Code und generiert automatisch den Reflection-API-Code für Sie.

  • Fügen Sie einfach dp4j.jar zu Ihrem CLASSPATH hinzu.

  • Dp4j.jar enthält Anmerkungsprozessoren. Sie suchen nach den Methoden in Ihrem Code, die mit der Annotation @Test JUnit versehen sind.

  • Dp4j analysiert den Code dieser Methoden. Wenn Sie feststellen, dass Sie illegal auf private Methoden zugreifen, wird Ihre ungültige private Methodenreferenz durch entsprechenden Code ersetzt, der die Reflection-API von Java verwendet.

Weitere Details hier

2
VdeX

Um sich einen Gedanken von Andy Hunt zu leihen, müssen selbst Ihre privaten Methoden einen Nebeneffekt haben, an dem Sie interessiert sind. Sie müssen also von einer öffentlichen Methode aus aufgerufen werden und eine interessante Aufgabe ausführen, durch die sich der Status Ihres Objekts ändert . Testen Sie diese Zustandsänderung.

Angenommen, Sie haben die öffentliche Methode pubMethod und die private Methode privMethod. Wenn Sie pubMethod aufrufen, ruft es wiederum privMethod auf, um eine Aufgabe auszuführen (möglicherweise analysiert es einen String). Die pubMethod verwendet dann diesen analysierten String, um die Werte der Mitgliedsvariablen auf irgendeine Weise festzulegen oder ihren eigenen Rückgabewert zu beeinflussen. Testen Sie, indem Sie nach dem gewünschten Effekt auf den Rückgabewert von pubMethod oder nach Member-Variablen suchen (möglicherweise mithilfe von Accessoren, um auf sie zuzugreifen).

2
Wil Doane

Ich bin auf das gleiche Problem gestoßen und das "Wenn es privat sein muss, sollte es wahrscheinlich umgestaltet werden", stimmt mir nicht. 

Angenommen, Sie verfügen über eine Art Funktionalität, die Sie in gewisser Weise innerhalb der Klasse trennen möchten. Nehmen wir zum Beispiel an, ich habe so etwas:

public class HolderOfSomeStrings{

    private List<String> internal_values;

    public List<String> get()
    {
       List<String> out = new ArrayList<String>();
       for (String s:internal_values)
       { 
           out.add(process(s));
       }
      return get;
    }

   private static String process(String input)
   {
     //do something complicated here that other classes shouldn't be interested in
    }

}

Der Punkt hier ist, dass junit mich zwingt, den Prozess öffentlich zu machen oder zumindest zu schützen oder ihn in seine eigene Nutzungsklasse zu stecken. Wenn es sich jedoch um eine Art interne Logik von HolderOfSomeStrings handelt, ist mir keineswegs klar, dass dies richtig ist - es scheint mir, dass dies privat sein sollte und es den Code irgendwie sichtbar macht. 

2
Steve B.

Verwenden Sie Reflektion wie oben, um private Methoden zu testen. Wenn wir TDD befolgen, sollten Sie private Methoden testen, da TDD impliziert, dass es später keine Überraschungen mehr geben würde .. Daher sollte man nicht warten, bis er seine öffentliche Methode zum Testen von Privaten beendet Und dies hilft bei differenzierten Regressionstests beim Re-Factoring.

1

Sie können TestNG anstelle von JUnit verwenden. Dabei ist es egal, ob die Methode privat oder öffentlich ist.

1
Pierre Gardin

Ich stimme @duffymo absolut zu, dass Code aus der Sicht des Kunden getestet werden sollte (obwohl er sagt, er habe aufgehört, auf diese Weise zu denken). Unter diesem Gesichtspunkt haben private und andere jedoch unterschiedliche Bedeutungen. Der Client einer privaten Methode ist die Klasse selbst. Ich bevorzuge es, sie über die externe (öffentliche/Paket-geschützte) API zu testen. Geschützte und paketgeschützte Member sind jedoch für externe Clients vorhanden. Daher teste ich sie mit Fälschungen, die die besitzende Klasse erben oder im selben Package liegen.

0
Cagatay Kalan

In Übereinstimmung mit so ziemlich jedem Beitrag - Sie sollten wahrscheinlich einen Refactor durchführen und wahrscheinlich keinen privaten Test durchführen, außer durch die Öffentlichkeit.

Stellen Sie sich Ihre Klasse selbst als "Einheit" vor, nicht als Methode. Sie testen die Klasse und können unabhängig davon, wie ihre öffentlichen Methoden aufgerufen werden, einen gültigen Status beibehalten.

Das Aufrufen privater Methoden kann die Kapselung zerstören und die Tests tatsächlich ungültig machen.

0
Bill K

Suchen Sie nach "PrivateAccessor.invoke". Mein Code importiert ihn aus "junitx.util", aber ich weiß nicht, woher er stammt.

0
Paul Tomblin

Als eine Art Ableger dieser Sache, und ich bin mir nicht sicher, wo alle auf die gesamte "Polyglot-Programmierung" -Problematik eingehen, aber Groovy-Tests sind in Junit lauffähig und ignorieren das gesamte öffentliche/nicht öffentliche Problem. Anmerkung, es wurde offiziell als "Fehler" eingestuft, aber als sie versuchten, es zu reparieren, wurde ein solcher Sturm ausgelöst, dass es so zurückgesetzt wurde, wie es ursprünglich war.

0
mezmo