3227 Stimmen

Wie teste ich eine Klasse, die private Methoden, Felder oder innere Klassen hat?

Wie kann ich mit JUnit eine Klasse testen, die interne private Methoden, Felder oder verschachtelte Klassen hat?

Es scheint schlecht, den Zugriffsmodifikator für eine Methode zu ändern, nur um einen Test durchführen zu können.

327 Stimmen

Der beste Weg, eine private Methode zu testen, ist, sie nicht direkt zu testen

30 Stimmen

3 Stimmen

Warum sollte eine private Funktion getestet werden? Sie wird ohnehin automatisch getestet (und muss getestet werden), wenn Sie die Funktion/Methode testen, die die private Funktion verwendet.

18voto

Joe Punkte 796

Wenn Sie JUnit verwenden, werfen Sie einen Blick auf junit-addons . Es hat die Fähigkeit, das Java-Sicherheitsmodell zu ignorieren und auf private Methoden und Attribute zuzugreifen.

18voto

bmurphy1976 Punkte 27336

Ich tendiere dazu, private Methoden nicht zu testen. Das ist der Wahnsinn. Ich persönlich glaube, dass Sie nur Ihre öffentlich zugänglichen Schnittstellen testen sollten (und das schließt geschützte und interne Methoden ein).

16voto

GROX13 Punkte 4113

Ich würde vorschlagen, dass Sie Ihren Code ein wenig umgestalten. Wenn Sie anfangen müssen, über die Verwendung von Reflexion oder andere Dinge nachzudenken, nur um Ihren Code zu testen, läuft etwas mit Ihrem Code schief.

Sie haben verschiedene Arten von Problemen erwähnt. Beginnen wir mit privaten Feldern. Im Falle von privaten Feldern hätte ich einen neuen Konstruktor hinzugefügt und Felder in diesen injiziert. Anstatt dessen:

public class ClassToTest {

    private final String first = "first";
    private final List<String> second = new ArrayList<>();
    ...
}

Ich hätte das hier benutzt:

public class ClassToTest {

    private final String first;
    private final List<String> second;

    public ClassToTest() {
        this("first", new ArrayList<>());
    }

    public ClassToTest(final String first, final List<String> second) {
        this.first = first;
        this.second = second;
    }
    ...
}

Dies ist selbst bei altem Code kein Problem. Alter Code wird einen leeren Konstruktor verwenden, und wenn Sie mich fragen, wird der überarbeitete Code sauberer aussehen, und Sie werden in der Lage sein, notwendige Werte in Tests ohne Reflexion zu injizieren.

Nun zu den privaten Methoden. Meiner persönlichen Erfahrung nach hat eine private Methode, die man zum Testen stubben muss, in dieser Klasse nichts zu suchen. Ein übliches Muster wäre in diesem Fall, dass man wickeln innerhalb einer Schnittstelle, wie Callable und dann übergeben Sie diese Schnittstelle auch im Konstruktor (mit diesem Trick mit mehreren Konstruktoren):

public ClassToTest() {
    this(...);
}

public ClassToTest(final Callable<T> privateMethodLogic) {
    this.privateMethodLogic = privateMethodLogic;
}

Meistens sieht alles, was ich geschrieben habe, so aus, als ob es sich um ein Dependency Injection Pattern handelt. Meiner persönlichen Erfahrung nach ist es beim Testen sehr nützlich, und ich denke, dass diese Art von Code sauberer ist und leichter zu pflegen ist. Dasselbe würde ich über verschachtelte Klassen sagen. Wenn eine verschachtelte Klasse schwere Logik enthält, wäre es besser, sie als private Klasse in ein Paket zu verschieben und sie in eine Klasse zu injizieren, die sie benötigt.

Es gibt auch einige andere Entwurfsmuster, die ich beim Refactoring und bei der Wartung von Legacy-Code verwendet habe, aber es hängt alles von den zu testenden Fällen Ihres Codes ab. Die Verwendung von Reflection ist meistens kein Problem, aber wenn man eine Unternehmensanwendung hat, die intensiv getestet wird und die Tests vor jedem Einsatz ausgeführt werden, wird alles sehr langsam (das ist einfach ärgerlich und ich mag so etwas nicht).

Es gibt auch Setter Injection, aber ich würde nicht empfehlen, sie zu verwenden. Ich würde besser mit einem Konstruktor bleiben und alles initialisieren, wenn es wirklich notwendig ist, so dass die Möglichkeit für die Injektion von notwendigen Abhängigkeiten.

2 Stimmen

Nicht einverstanden mit der ClassToTest(Callable) Injektion. Das macht ClassToTest komplizierter. Halten Sie es einfach. Auch das erfordert dann jemand anders zu erzählen ClassToTest etwas, das ClassToTest der Chef sein sollte. Es gibt einen Platz für das Einfügen von Logik, aber das ist er nicht. Sie haben die Klasse nicht einfacher, sondern schwieriger zu pflegen gemacht.

0 Stimmen

Außerdem ist es von Vorteil, wenn Ihre Testmethode X erhöht nicht X Komplexität bis zu dem Punkt, an dem sie mehr Probleme verursachen könnte... und daher zusätzliche Tests erfordert... was, wenn Sie auf eine Weise implementiert haben, die mehr Probleme verursachen kann... (dies ist keine Endlosschleife; jede Iteration ist wahrscheinlich kleiner als die vorherige, aber immer noch ärgerlich)

2 Stimmen

Könnten Sie erklären, warum es die Klasse ClassToTest schwieriger zu pflegen? Eigentlich macht es Ihre Anwendung leichter zu pflegen, was schlagen Sie vor, eine neue Klasse für jeden verschiedenen Wert, den Sie in first und 'zweite' Variablen?

15voto

Yuli Reiri Punkte 626

Hier ist meine generische Funktion zum Testen privater Felder:

protected <F> F getPrivateField(String fieldName, Object obj)
    throws NoSuchFieldException, IllegalAccessException {
    Field field =
        obj.getClass().getDeclaredField(fieldName);

    field.setAccessible(true);
    return (F)field.get(obj);
}

15voto

Olcay Tarazan Punkte 881

Nachstehend finden Sie ein Beispiel;

Die folgende Importanweisung sollte hinzugefügt werden:

import org.powermock.reflect.Whitebox;

Jetzt können Sie das Objekt direkt übergeben, das die private Methode, den aufzurufenden Methodennamen und zusätzliche Parameter enthält (siehe unten).

Whitebox.invokeMethod(obj, "privateMethod", "param1");

CodeJaeger.com

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

Powered by:

X