Wie injiziere ich Testobjekte, wenn die realen Objekte dynamisch erstellt werden?

8

Ich möchte eine Klasse mithilfe der Abhängigkeitsinjektion testbar machen. Die Klasse erstellt jedoch zur Laufzeit mehrere Objekte und übergibt unterschiedliche Werte an ihren Konstruktor. Hier ist ein vereinfachtes Beispiel:

public abstract class Validator {
    private ErrorList errors;

    public abstract void validate();

    public void addError(String text) {
        errors.add(
            new ValidationError(text));
    }

    public int getNumErrors() {
        return errors.count()
    }
}

public class AgeValidator extends Validator {
    public void validate() {
        addError("first name invalid");
        addError("last name invalid");
    }
}

(Es gibt viele andere Unterklassen von Validator.)

Wie kann ich dies am besten ändern, damit ich anstelle von ValidationError ein gefälschtes Objekt einfügen kann?

Ich kann eine AbstractValidationErrorFactory erstellen und stattdessen die Factory injizieren. Das würde funktionieren, aber es sieht so aus, als würde ich für jede Abhängigkeit dieser Art Tonnen kleiner Fabriken und Fabrikschnittstellen schaffen. Gibt es einen besseren Weg?

JW01
quelle
Was versuchst du zu testen? Dass der Validator ValidationErrors erstellt? Faking ValidationError hilft nicht. Aus heutiger Sicht ist der Validator nicht sehr nützlich, da Sie validate aufrufen können, die Fehler jedoch nicht generiert werden können.
Michael Brown
Ich habe es weggelassen, aber es gibt andere Methoden, die den Zugriff auf das ErrorList-Objekt ermöglichen. Also kann ich damit testen.
JW01
Siehe stackoverflow.com/questions/4859523/... für verwandte Informationen
blueberryfields

Antworten:

4

Die einfachste Änderung, um Ihre Klasseneinheit testbar zu machen, ist:

  1. (Extrahieren Sie den Erstellungscode in eine neue (virtuelle) Methode - diese haben Sie bereits)
  2. Unterklasse der getesteten Klasse, wobei die Erstellungsmethode überschrieben wird, um ein geeignetes Scheinobjekt anstelle des realen zu erstellen
  3. schreibe deine Unit Tests :-)

Das ist natürlich nicht sehr schön. Sie können die Klasse jedoch mit Komponententests abdecken. Anschließend können Sie mit der Umgestaltung des idealen Designs beginnen, ohne befürchten zu müssen, dass der Code durch tiefere Änderungen beschädigt wird.

Obligatorische Referenz: Effektiv mit Legacy Code arbeiten .

Über dieses ideale Design ist jedoch schwer viel zu sagen, da Ihrem Beispiel Details und Kontext fehlen. Wenn Sie uns mehr darüber erzählen, was Sie testen möchten und welche Rolle die getestete Klasse spielt, können wir Ihnen möglicherweise weiterhelfen.

Péter Török
quelle
Das war mein allgemeiner Ansatz, aber in diesem Fall gibt es mehrere Unterklassen von Validator, und der Test erfordert viel Setup. Wenn ich eine Testunterklasse erstelle, muss ich auch eine Testunterklasse für jede echte Unterklasse von Validator erstellen. Also werde ich am Ende einen Großteil meiner Setup-Arbeit duplizieren. Aber vielleicht ist das in diesem Fall notwendig.
JW01
2

Normalerweise ist die Lösung für dieses Problem das Anbietermuster:

public class Validator {
    public Provider<ValidationError> errorProvider;

    public void addError(String text) {
        errors.add(errorProvider.get(text));
    }
}

Der Anbieter ähnelt der Fabrik. In diesem Fall wird bei ValidationErrorjedem getAufruf neu zurückgegeben.

Weitere Informationen finden Sie im Buch Abhängigkeitsinjektion in Abschnitt 3.3.2 (Reinjektion mit dem Provider-Muster).

Guice hat zum Beispiel einen Anbieter dafür. Wenn Sie etwas Anspruchsvolleres wünschen, können Sie AssistedInject verwenden .

Einfacher Engel
quelle