2013-04-04 7 views
29

Jak Mock następujące klasy:Jak mock klasy, która implementuje wiele interfejsów

UserRepository : GenericRepository<User>, IUserRepository 


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

Używam Moq i jestem zdezorientowany, jak obsługiwać wiele interfejsów poprawnie.

+0

Czy istnieje związek pomiędzy 'IUserRepository' i' IGenericRepository'? Czy jest wymagane, aby obiekt implementujący 'IUserRepository' również implementował' IGenericRepository '? –

+0

Jak wygląda kod produkcyjny, który próbujesz przetestować? –

Odpowiedz

3

Nie kpisz z klas, kpisz z interfejsów. W twoim przypadku możesz mieć dwie makiety - jedną, która kpi IUserRepository i która kpi IGenericRepository<User>. Nie muszą być tym samym obiektem - jeśli muszą być tym samym obiektem, może to być wada projektowa.

+0

+1 za zwięzłość. Nie dbamy o klasę, ponieważ wyraźnie NIE uruchamiamy swojego kodu, tylko udajemy. –

+0

Aby to wyjaśnić, można oczywiście wyśmiać klasę (zwłaszcza klasę abstrakcyjną), przynajmniej wszelkie wirtualne metody/właściwości tego obiektu. Zgadzam się z twoim punktem, ale wyśmiewałbyś (publiczny/chroniony/wewnętrzny) * interfejs * klasy. – Serguei

+3

Jedną z zalet interfejsów jest to, że jeden obiekt może implementować wiele interfejsów. Nie jest to z natury wadą konstrukcyjną. Mogę wdrożyć IDisposable i IEnumerable. Jest również często używany z [interfejsami do tagowania] (https://en.wikipedia.org/wiki/Marker_interface_pattern), w którym to przypadku wyśmiewanie obiektu implementującego wiele interfejsów jest konieczne do przeprowadzenia niektórych testów. Na przykład używam interfejsu IEncrypted do oznaczania DTO, które muszą być zaszyfrowane po serializacji, ale wszystkie DTO muszą implementować nasz interfejs IMessage (który faktycznie ma właściwości do tranzytu). – blockloop

3

Jeśli dobrze rozumiem pytanie, chcesz mieć pojedynczą instancję UserRepository makiety, a dla celów testu, konfiguracja połączenia z metod zarówno z interfejsem IGenericRepository<TEntity> a interfejsem IUserRepository.

można zaimplementować wiele interfejsów z pojedynczym udawanym przykład tak:

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); 

Jednakże, jak D Stanley wskazał, potrzeba na to jest prawdopodobnie wskazanie, że istnieje luka w projekcie.

22

Istnieje mechanizm wbudowany w Moq do obsługi wielu interfejsów.

że mamy interfejs IFoo i implementację tego samego Foo. Mamy również ClientOne, który używa IFoo.

Następnie mają interfejsu IFooBar: IFoo, implementacja FooBar: Foo, IFooBar a ClientTwo który wykorzystuje IFooBar.

Podczas tworzenia testu end-to-end dla systemu mamy IFooBar, ClientOne i ClientTwo. Jak <>() funkcja pozwala nam korzystać z Mock <IFooBar> jako Mock <IFoo>.

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

Spójrz na https://github.com/Moq/moq4/wiki/Quickstart

zaawansowanych funkcji

// 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

Dodając do tej odpowiedzi, jeśli chcesz przekazać próbę do metody, która ma IFoo, możesz również zmienić IFoo i IDisposable, tj: var disposableFoo = new Mock (); var foo = disposableFoo.As (); – suhendri

+0

To zdecydowanie powinna być akceptowana odpowiedź. –