2013-04-04 7 views
29

implementiert Wie die folgende Klasse verspotten:Wie eine Klasse zu verspotten, die mehrere Schnittstellen

UserRepository : GenericRepository<User>, IUserRepository 


public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class 

ich Moq verwende, und ich bin verwirrt, wie mehrere Schnittstellen richtig zu handhaben.

+0

Gibt es einen Zusammenhang zwischen 'IUserRepository' und' IGenericRepository'? Ist es erforderlich, dass ein Objekt, das 'IUserRepository' implementiert, auch 'IGenericRepository ' 'implementiert? –

+0

Wie sieht der Produktionscode aus, den Sie testen möchten? –

Antwort

3

Sie verspotten nicht Klassen, verspotten Sie Schnittstellen. In Ihrem Fall könnten Sie zwei Mocks haben - eine, die IUserRepository verspottet und eine, die IGenericRepository<User> verspottet. Sie sollten nicht notwendigerweise das gleiche Objekt sein - wenn sie das gleiche Objekt sein müssen, dann kann es ein Designfehler sein.

+0

+1 für Prägnanz. Die Klasse interessiert uns nicht, da wir explizit NICHT den Code ausführen, sondern nur den Mock. –

+0

Nur zur Klarstellung, Sie können sicherlich eine Klasse (vor allem eine abstrakte Klasse), zumindest virtuelle Methoden/Eigenschaften davon verspotten. Ich stimme Ihrem Punkt zu, Sie würden sich über die (öffentliche/geschützte/interne) * Schnittstelle * der Klasse lustig machen. – Serguei

+3

Einer der Vorteile von Interfaces ist, dass ein Objekt viele Schnittstellen implementieren kann. Das ist nicht von Natur aus ein Konstruktionsfehler. Ich könnte IDisposable und IEnumerable implementieren. Es wird auch oft mit [tagging interfaces] (https://en.wikipedia.org/wiki/Marker_interface_pattern) verwendet. In diesem Fall ist es für einige Tests notwendig, ein Objekt zu verspotten, das mehrere Interfaces implementiert. Zum Beispiel verwende ich eine IEncrypted-Schnittstelle zum Markieren von DTOs, die nach der Serialisierung verschlüsselt werden müssen, aber alle DTOs müssen unsere IMessage-Schnittstelle implementieren (die tatsächlich über Eigenschaften für die Übertragung verfügt). – blockloop

3

Wenn ich die Frage richtig verstanden habe, wollen Sie eine einzelne Instanz von Mock UserRepository haben, und für die Zwecke eines Tests Setup ruft Methoden sowohl von der IGenericRepository<TEntity>-Schnittstelle und der IUserRepository Schnittstelle.

Sie können mehrere Schnittstellen mit einer einzigen Mock-Instanz wie folgt implementieren:

var genericRepositoryMock = new Mock<IGenericRepository<User>>(); 
genericRepositoryMock.Setup(m => m.CallGenericRepositoryMethod()).Returns(false); 

var userRepositoryMock = genericRepositoryMock.As<IUserRepository>(); 
userRepositoryMock.Setup(m => m.CallUserRepositoryMethod()).Returns(true); 

jedoch als D Stanley wies darauf hin, das ist die Notwendigkeit zu tun, ist wahrscheinlich ein Hinweis, dass ein Fehler es in Ihrem Design ist.

22

In Moq ist ein Mechanismus für den Umgang mit mehreren Schnittstellen integriert.

Sagen wir eine Schnittstelle IFoo und eine Implementierung der gleichen Foo haben. Wir haben auch ClientOne, die IFoo verwendet.

Wir haben dann eine Schnittstelle IFooBar haben: IFoo, eine Implementierung FooBar: Foo, IFooBar und ein ClientTwo die IFooBar verwendet.

Wenn ein End-to-End-Test für das System schaffen wir haben eine IFooBar, ClientOne und ClientTwo. Die Als <>() Funktion ermöglicht es uns, die Mock <IFooBar> als Mock <IFoo> zu verwenden.

public interface IFoo { 
    int Id { get; } 
} 

public class Foo : IFoo { 
    public int Id { 
     get { return 1; } 
    } 
} 

public interface IFooBar : IFoo { 
    string Name { get; } 
} 

public class FooBar : Foo, IFooBar { 
    public string Name { 
     get { return "AName"; } 
    } 
} 

public class ClientOne { 
    private readonly IFoo foo; 

    public ClientOne(IFoo foo) { 
     this.foo = foo; 
    } 

    public string Details { 
     get { return string.Format("Foo : {0}", foo.Id); } 
    } 

} 

public class ClientTwo { 
    private readonly IFooBar fooBar; 

    public ClientTwo(IFooBar fooBar) { 
     this.fooBar = fooBar; 
    } 

    public string Details { 
     get { return string.Format("Foo : {0}, Bar : {1}", fooBar.Id, fooBar.Name); } 
    } 

} 


[TestMethod] 
public void TestUsingBothClients() { 

    var fooBarMock = new Mock<IFooBar>(); 
    var fooMock = fooBarMock.As<IFoo>(); 

    fooBarMock.SetupGet(mk => mk.Id).Returns(1); 
    fooBarMock.SetupGet(mk => mk.Name).Returns("AName"); 

    var clientOne = new ClientOne(fooMock.Object); 
    var clientTwo = new ClientTwo(fooBarMock.Object); 

    Assert.AreEqual("Foo : 1", clientOne.Details); 
    Assert.AreEqual("Foo : 1, Bar : AName", clientTwo.Details); 

} 
35

Werfen Sie einen Blick auf https://github.com/Moq/moq4/wiki/Quickstart

Erweiterte Funktionen

// implementing multiple interfaces in mock 
var foo = new Mock<IFoo>(); 
var disposableFoo = foo.As<IDisposable>(); 
// now IFoo mock also implements IDisposable :) 
disposableFoo.Setup(df => df.Dispose()); 
+1

Wenn Sie zu dieser Antwort hinzufügen, wenn Sie den Schein an eine Methode weitergeben müssen, die IFoo übernimmt, können Sie auch IFoo und IDisposable wechseln, dh: var disposableFoo = new Mock (); var foo = disposableFoo.As (); – suhendri

+0

Dies sollte definitiv die akzeptierte Antwort sein. –

Verwandte Themen