2017-06-05 5 views
1

Ich verwende Autofac in meinem Projekt, aber ich bin nicht in der Lage, einen Komponententest für eine bestimmte Klasse zu tun.Wie teste ich eine Klasse, die IoC-Container-Klassen verwendet

Betrachten Sie das folgende Szenario:

//Class to be tested 
public Class A 
{ 
    private SomeAutoFacClass B; 

    public void DoSomething() 
    { 
    B = scope.Resolve<ClassName>();// Resolve the object needed 
    // Do something with instance B 
    } 
} 

// Test class 
public Class ATest 
{ 
    private A a; 

    [test] 
    public void TestMethod() 
    { 
    a.DoSomething();//*This method causes a null reference exception as it tries to resolve the objects* 
    } 
} 

In dem obigen Code, ich bin zu Einheit Testfall nicht in der Lage aufgrund der Dependency Injection, die nur spezifisch auf die jeweilige Klasse. Wie löse ich das? Ich habe auch versucht, einen Autofaccontainer mit Moq zu erstellen. Aber das scheitert auch.

Antwort

4

Der Grund, warum Sie Ihre Klasse nicht testen können, liegt daran, dass Ihre Klasse eine Abhängigkeit von Ihrem DI-Container übernimmt. Dies ist eine Implementierung der Service Locator anti-pattern. Es ist ein Anti-Muster, weil:

das Problem mit Service Locator ist, dass es eine Klasse Abhängigkeiten verbirgt, Laufzeitfehler statt Kompilierung-Fehler verursachen, sowie Herstellung der Code schwieriger zu warten weil es unklar wird, wann Sie eine brechende Veränderung einführen würden.

Stattdessen entwerfen Sie Ihre Klassen um

  • Constructor Injection falls die Klasse ist eine Komponente (eine Klasse, die die Anwendungen Verhalten enthält), wo Sie die Abhängigkeiten zu injizieren, die eine Klasse direkt benötigt durch den Konstruktor
  • Methode Injection wenn die Klasse ein datenzentrisches Objekt wie eine Entität ist, was bedeutet, dass die Abhängigkeit an die öffentliche Methode einer solchen Klasse, wo der Consumin geliefert wird Die Klasse g verwendet diese Abhängigkeit nur, speichert sie aber nicht.

Komponenten werden von Ihrem DI Container verbauten und sind in Ihrem Composition Root, während datenzentrische Objekte registriert sind new in Code außerhalb der Zusammensetzung Root-ed up. In diesem Fall müssen Sie eine Abhängigkeit zu einem bereits konstruierten Objekt übergeben.

Falls Sie bauen und eine Komponente testen, würde der Code der Regel wie folgt aussehen:

public class ComponentA 
{ 
    private ClassName b; 

    public ComponentA(ClassName b) 
    { 
     this.b = b; 
    } 

    public void DoSomething() 
    { 
     // Do something with instance B 
    } 
} 

// Test class 
public Class ATest 
{ 
    [test] 
    public void TestMethod() 
    { 
     // Arrange 
     B b = new FakeB(); 

     var a = new ComponentA(b); 

     // Act 
     a.DoSomething(); 

     // Assert 
     // Check whether be was invoked correctly. 
    } 
} 

Falls Sie bauen und eine Daten-zentrierte Objekt testen, die für eine seiner Operationen eine Abhängigkeit erfordert, Ihre Code würde in der Regel wie folgt aussehen:

public class EntityA 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 

    public void DoSomething(ClassName b) 
    { 
     // Do something with instance B 
    } 
} 

// Test class 
public Class ATest 
{ 
    [test] 
    public void TestMethod() 
    { 
     // Arrange 
     B b = new FakeB(); 

     var a = new EntityA { Name = "Bert", Age = 56 }; 

     // Act 
     a.DoSomething(b); 

     // Assert 
     // Check whether be was invoked correctly. 
    } 
} 

so Ihre erste Frage zu beantworten:

wie d o Unit-Test eine Klasse, die IoC-Container-Klassen verwendet

Sie nicht. Ihr Anwendungscode sollte nicht von dem DI-Container abhängen, da dies zu allen Arten von Komplikationen führt, wie z. B. schwierig zu testen.

+0

Das war eine sehr gute Erklärung! mehr davon auf SO. – Seabizkit

-2

Mit einem IoC-Container sollten Sie versuchen, IoC zu verwenden. Normalerweise ist dies entweder eine Konstruktorinjektion oder eine Eigenschaftsinjektion, abhängig davon, was Ihr Container für die automatische Injektion unterstützt.

Ein Muster, das ich für diese verwende ich „lazy Eigenschaft injection“ nennen, wo ich Konstruktordeklaration injizieren mein Container als Registry zu handeln, dann verwenden Sie die faulen Auflösung auf Nutzungsart ..

Was es wie folgt aussieht:

Wenn Sie nun eine Methode dieser Klasse testen, initialisiert Ihr Test Ihren MyService mit einem Mock. Wenn ich im Test bin, habe ich den IoCContainer zu einem Mock() initialisiert, der, falls vorhanden .Resolve <> Aufrufe werden. Dadurch werden Szenarien erfasst, in denen der getestete Code möglicherweise geändert wurde, um eine neue Abhängigkeit zu verwenden, die nicht ausgeheckt wurde.

Der Vorteil dieses Ansatzes besteht darin, dass eine Klasse häufig mehrere Einzweckmethoden hat und daher eine Reihe von Abhängigkeiten benötigt. In Anwendungen wie Webanforderungen, bei denen möglicherweise nur eine Methode aufgerufen wird, die eine Abhängigkeit benötigt, ruft die Abhängigkeitsauflösung nur die Abhängigkeiten vom Container ab, die benötigt werden, nicht alle. (Das heißt, wenn Sie die Konstruktorinjektion mit 8 Abhängigkeiten verwenden, müssen alle 8 zur Laufzeit aufgelöst werden, auch wenn nur 1 verwendet wird.) Dies vereinfacht auch das Komponententesten, um nur das zu verspotten, von dem Sie wissen, dass es benötigt wird.

+3

"Ein Muster, das ich dafür verwende, nenne ich" faule Eigenschaft Injektion ", wo ich Konstruktor-injiziere meinen Container, um als eine Registrierung zu fungieren, dann verwenden Sie die faule Auflösung für die Verwendung von Eigentum". Dieses Muster hat bereits einen Namen, nämlich: ** Service Locator ** und [es ist ein Anti-Pattern] (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). – Steven

+0

Service Locators werden von einigen als Anti-Pattern betrachtet. Nichts in meinem Gebrauch des Behälters leckt in einer negativen Weise. Wenn der IoC-Container im gesamten Code referenziert wurde, würde ich die Verwendung des Containers, der nach einem Anti-Pattern riecht, befürworten, aber die Verwendung der Methode zum Auflösen der Abhängigkeiten über eine Kapselungseigenschaft ist nicht möglich. Persönlich würde ich argumentieren, dass die Verwendung eines IoC-Containers zum automatischen Injizieren eher ein Anti-Pattern ist, da es als Black Box wirkt, genau wie Abhängigkeiten aufgelöst und injiziert werden. –

Verwandte Themen