Unit-Test-Klassen mit Online-Funktionalität

23

Beim Unit-Testen von Funktionen einer Klasse mit privaten Funktionen, für die Online-Funktionen erforderlich sind. Wie würde man es testen?

Beispielsweise:

public class Foo
{
    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        CloudService c = new CloudService();
        int oval = c.getValueFromService();
        return oval;
    }
}

Wenn ich die Funktion testen würde: 'methodA ()', würde es versuchen, 'goOnlineToGetVal ()' zu verwenden, was wiederum versuchen würde, online zu gehen, wenn dieser Test ohne Funktionalität durchgeführt würde. Wie würde ich eine 100% ige Klassenabdeckung erreichen, ohne online zu gehen?

Ben verrückt nach Euden
quelle
Ruft Ihre Methode nur die Cloud-API auf oder erledigt sie zusätzliche Arbeit?
Winston Ewert
4
Abhängigkeitsspritze ?
PhaDaPhunk

Antworten:

76

new CloudService()

Und da ist dein Problem.

Modernes OO-Design empfiehlt, diese Art von Abhängigkeit weiterzugeben und nicht direkt zu konstruieren. Dies kann in die Funktion selbst oder zur Konstruktionszeit an die Klasse übergeben werden. Es könnte auch von einem Inversion of Control-Container erfasst oder aggregiert werden, wenn diese Komplexität gerechtfertigt ist.

An diesem Punkt wird es ziemlich trivial, einen Schein- / Fälschungsdienst zu absolvieren, um Sie während des Testens mit Ihren "Online" -Daten zu versorgen. Besser noch: Ihre Software ist so flexibel, dass Sie sie schnell anpassen können, falls ein (Regierungs-?) Kunde hinzukommt und die Cloud nicht für ihre Werte nutzen möchte. Oder Sie möchten einen Cloud-Anbieter für einen anderen sichern. Oder...

Telastyn
quelle
7
Ich glaube, ich bin dem Verstehen, was die verdammte Abhängigkeitsinjektion ist, jetzt viel näher gekommen.
Marczellm
Wo kann ich am besten alle Instanzen erstellen, die meine Anwendung benötigt? So nah wie möglich an der Spitze meiner Bewerbung? Bisher können Sie nur die Abhängigkeitsinjektion anwenden. Irgendwann müssen Sie die Instanzen selbst erstellen, anstatt sie als Argumente übergeben zu lassen.
Paul
3
@ Paul - Es kommt darauf an. Nach meiner Erfahrung sehen Anwendungen sehr oft wie binäre Bäume aus - eine Klasse / Funktion / Modul klebt zwei zusammen, um ein komplexeres Verhalten zu erzielen. Eine dieser "Kleber" -Klassen ist diejenige, die die konkrete Implementierung angibt, die wiederum von etwas darüber verwendet wird, das es mit etwas anderem verklebt, was wiederum ... bis Sie schließlich zu Ihrem Eintrittspunkt gelangt, der die großen Teile zusammenklebt zusammenhängend. Der "beste" Ort ist der Ort, an dem Ihr Code das tut, was er tun muss, ohne dass er etwas tut oder etwas darüber weiß, was er nicht tun sollte. Es wird variieren.
Telastyn
2
@Paul Normalerweise wird Ihre Anwendung in eine "Bibliothek" aufgeteilt, die direkt getestet wird und diese Art der Abhängigkeitsinjektion sowie anwendungsspezifischen Code verwenden sollte. Letzteres läuft in der Regel auf eine GUI, einen Webdienst oder eine Befehlszeilenschnittstelle hinaus, die die Bibliothek mit vom Benutzer bereitgestellten Daten direkt aufruft. Im Wesentlichen wird anwendungsspezifischer Code die konkreten Implementierungen erstellen, die die abhängigkeitsinjektierte Bibliothek erwartet.
Darkhogg
@Paul Schauen Sie sich IoC-Container wie Castle Windsor, StructureMap, Ninject und Unity an. Sie sind keineswegs die einzige Möglichkeit, eine Abhängigkeitsinjektion durchzuführen, aber sie können sehr informativ sein, wenn es darum geht, ein Objektdiagramm von oben nach unten
Ben Aaronson,
37

Ich würde es so implementieren:

public class Foo
{
    private ICloudService cloudService;

    public Foo(ICloudService s)
    {
       cloudService=s;
    }

    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        int oval = cloudService.getValueFromService();
        return oval;
    }
}

Die Schnittstelle ICloudServicekann entweder mit einem Mock zum Testen oder mit dem "echten" Cloud-Service implementiert werden. Wenn die Instanziierung new CloudService()für jeden Aufruf einer Drittanbieter-API obligatorisch getValueFromServiceist oder CloudServicevon einer Drittanbieter-API stammt, die nicht geändert werden kann, implementieren Sie einen Wrapper, leiten Sie ICloudServicedie entsprechenden Aufrufe ab und führen Sie sie aus .

Doc Brown
quelle
1
Dies ist definitiv der richtige Weg. Es gibt Klassenbibliotheken, die eine Schnittstelle nachahmen können und Ihnen die absolute Kontrolle über den Test geben.
Greg Burghardt