Frage

Ich habe also den Google-Testblog gelesen und dort steht, dass der globale Status schlecht ist und es schwierig macht, Tests zu schreiben.Ich glaube es – mein Code ist derzeit schwer zu testen.Wie vermeide ich also einen globalen Zustand?

Die wichtigsten Aufgaben, für die ich den globalen Status (so wie ich ihn verstehe) verwende, sind die Verwaltung wichtiger Informationen zwischen unserer Entwicklungs-, Akzeptanz- und Produktionsumgebung.Zum Beispiel habe ich eine statische Klasse namens "Global" mit einem statischen Mitglied namens "DbConnectionString". Wenn die Anwendung lädt, wird bestimmt, welche Verbindungszeichenfolge geladen werden soll, und populiert Globals.dbConnectionString.Ich lade Dateipfade, Servernamen und andere Informationen in die Globals-Klasse.

Einige meiner Funktionen basieren auf den globalen Variablen.Wenn ich also meine Funktionen teste, muss ich daran denken, zuerst bestimmte globale Werte festzulegen, sonst schlagen die Tests fehl.Das möchte ich gerne vermeiden.

Gibt es eine gute Möglichkeit, staatliche Informationen zu verwalten?(Oder verstehe ich den globalen Zustand falsch?)

War es hilfreich?

Lösung

Abhängigkeitsinjektion ist genau das, wonach Sie suchen.Anstatt diese Funktionen nach ihren Abhängigkeiten suchen zu lassen, fügen Sie die Abhängigkeiten in die Funktionen ein.Das heißt, wenn Sie die Funktionen aufrufen, übergeben Sie ihnen die gewünschten Daten.Auf diese Weise ist es einfach, ein Test-Framework um eine Klasse zu legen, da Sie bei Bedarf einfach Scheinobjekte einfügen können.

Es ist schwer, einen globalen Status zu vermeiden, aber der beste Weg, dies zu erreichen, besteht darin, Factory-Klassen auf der höchsten Ebene Ihrer Anwendung zu verwenden, und alles unterhalb dieser obersten Ebene basiert auf Abhängigkeitsinjektion.

Zwei Hauptvorteile:Erstens ist das Testen viel einfacher und zweitens ist Ihre Anwendung viel lockerer gekoppelt.Sie verlassen sich darauf, dass Sie gegen die Schnittstelle einer Klasse programmieren können und nicht gegen deren Implementierung.

Andere Tipps

Denken Sie daran, wenn Ihre Tests tatsächliche Ressourcen wie Datenbanken oder Dateisysteme umfassen, dann tun Sie dies auch Integrationstests statt Unit-Tests.Integrationstests erfordern einige Voreinstellungen, wohingegen Unit-Tests unabhängig voneinander ausgeführt werden können sollten.

Sie könnten die Verwendung eines Dependency-Injection-Frameworks wie Castle Windsor in Betracht ziehen, aber in einfachen Fällen können Sie möglicherweise einen Mittelweg-Ansatz wählen, wie zum Beispiel:

public interface ISettingsProvider
{
    string ConnectionString { get; }
}

public class TestSettings : ISettingsProvider
{        
    public string ConnectionString { get { return "testdatabase"; } };
}

public class DataStuff
{
    private ISettingsProvider settings;

    public DataStuff(ISettingsProvider settings)
    {
        this.settings = settings;
    }

    public void DoSomething()
    {
        // use settings.ConnectionString
    }
}

In Wirklichkeit würden Sie höchstwahrscheinlich aus Konfigurationsdateien in Ihrer Implementierung lesen.Wenn Sie dazu bereit sind, ist ein vollständiges DI-Framework mit austauschbaren Konfigurationen die richtige Wahl, aber ich denke, das ist zumindest besser als die Verwendung von Globals.ConnectionString.

Tolle erste Frage.

Die kurze Antwort:Stellen Sie sicher, dass Ihre Anwendung von ALLEN Eingaben (einschließlich impliziter) bis zu ihren Ausgaben eine Funktion ist.

Das von Ihnen beschriebene Problem scheint kein globaler Zustand zu sein.Zumindest kein veränderlicher Zustand.Was Sie beschreiben, scheint eher das zu sein, was oft als „das Konfigurationsproblem“ bezeichnet wird, und es gibt eine Reihe von Lösungen.Wenn Sie Java verwenden, möchten Sie möglicherweise nach leichten Injektions-Frameworks wie suchen Guice.In Scala wird dies normalerweise mit gelöst implizit.In einigen Sprachen können Sie ein anderes Programm laden, um Ihr Programm zur Laufzeit zu konfigurieren.So haben wir früher in Smalltalk geschriebene Server konfiguriert, und ich verwende einen in Haskell geschriebenen Fenstermanager namens Xmonad, dessen Konfigurationsdatei nur ein weiteres Haskell-Programm ist.

Hier ein Beispiel für die Abhängigkeitsinjektion in einer MVC-Umgebung:

index.php

$container = new Container();
include_file('container.php');

container.php

container.add("database.driver", "mysql");
container.add("database.name","app");

...

$container.add(new Database($container->get('database.driver', "database.name")), 'database');
$container.add(new Dao($container->get('database')), 'dao');
$container.add(new Service($container->get('dao')));
$container.add(new Controller($container->get('service')), 'controller');

$container.add(new FrontController(),'frontController');

index.php geht hier weiter:

$frontController = $container->get('frontController');
$controllerClass = $frontController->getController($_SERVER['request_uri']);
$controllerAction = $frontController->getAction($_SERVER['request_uri']);
$controller = $container->get('controller');
$controller->$action();

Dort haben Sie es, der Controller hängt von einem Service -Layer -Objekt ab, das von einem DAO -Objekt (Datenzugriffsobjekt) abhängt, das von einem Datenbankobjekt abhängt, wobei der Datenbank -Treiber, Name usw. abhängt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top