it-swarm.com.de

Durch das Verspotten wird die Handhabung im Produktionscode eingeführt

Angenommen, eine IReader-Schnittstelle, eine Implementierung der IReader-Schnittstelle ReaderImplementation und eine Klasse ReaderConsumer, die Daten vom Reader verwendet und verarbeitet.

public interface IReader
{
     object Read()
}

Implementierung

public class ReaderImplementation
{
    ...
    public object Read()
    {
        ...
    }
}

Verbraucher:

public class ReaderConsumer()
{
    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // read some data
    public object ReadData()
    {
        IReader reader = new ReaderImplementation(this.location)
        data = reader.Read()
        ...
        return processedData    
    }
}

Zum Testen von ReaderConsumer und der Verarbeitung verwende ich ein Modell von IReader. So wird ReaderConsumer:

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // mock constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader
    }

    // read some data
    public object ReadData()
    {
        try
        {
            if(this.reader == null)
            {
                 this.reader = new ReaderImplementation(this.location)
            }

            data = reader.Read()
            ...
            return processedData    
        }
        finally
        {
            this.reader = null
        }
    }
}

In dieser Lösung führt Mocking einen if-Satz für den Produktionscode ein, da nur der Mocking-Konstruktor eine Instanz der Schnittstelle bereitstellt.

Während des Schreibens stelle ich fest, dass der try-finally-Block etwas unabhängig ist, da er dazu dient, den Benutzer zu behandeln, der den Speicherort während der Laufzeit der Anwendung ändert.

Insgesamt fühlt es sich stinkend an, wie könnte es besser gehandhabt werden?

15
kristian mo

Verschieben Sie diese Zeile, anstatt den Reader über Ihre Methode zu initialisieren

{
    this.reader = new ReaderImplementation(this.location)
}

In den standardmäßigen parameterlosen Konstruktor.

public ReaderConsumer()
{
    this.reader = new ReaderImplementation(this.location)
}

public ReaderConsumer(IReader reader)
{
    this.reader = reader
}

Es gibt keinen "Scheinkonstruktor". Wenn Ihre Klasse eine Abhängigkeit hat, die erfordert, um zu funktionieren, sollte der Konstruktor entweder dieses Ding bereitstellen oder es erstellen.

67
RubberDuck

Sie benötigen nur den einzelnen Konstruktor:

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader;
    }

in Ihrem Produktionscode:

var rc = new ReaderConsumer(new ReaderImplementation(0));

in deinem Test:

var rc = new ReaderConsumer(new MockImplementation(0));
54
Ewan

Untersuchen Sie die Abhängigkeitsinjektion und die Inversion der Kontrolle

Sowohl Ewan als auch RubberDuck haben ausgezeichnete Antworten. Aber ich wollte einen anderen Bereich erwähnen, der untersucht werden soll: Dependency Injection (DI) und Inversion of Control (IoC). Beide Ansätze verschieben das aufgetretene Problem in ein Framework/eine Bibliothek, sodass Sie sich darüber keine Sorgen machen müssen.

Ihr Beispiel ist einfach und schnell erledigt, aber Sie werden unweigerlich darauf aufbauen und entweder Tonnen von Konstruktoren oder Initialisierungsroutinen erhalten, die wie folgt aussehen:

var foo = neuer Foo (neuer Balken (neuer Baz (), neuer Quz ()), neuer Foo2 ());

Mit DI/IoC verwenden Sie eine Bibliothek, in der Sie die Regeln für die Zuordnung von Schnittstellen zu Implementierungen festlegen können. Dann sagen Sie einfach "Give me a Foo" und es wird herausgefunden, wie Sie alles verkabeln können.

Es gibt viele sehr freundliche IoC-Container (wie sie genannt werden), und ich werde einen empfehlen, den man sich ansehen sollte, aber bitte erkunden Sie ihn, da es sehr viele gute Möglichkeiten gibt.

Ein einfacher Anfang ist:

http://www.ninject.org/

Hier ist eine Liste zum Erkunden:

http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

13
Reginald Blue