2013-04-04 3 views
29

Как поиздеваться следующий класс:Как издеваются класс, который реализует несколько интерфейсов

UserRepository : GenericRepository<User>, IUserRepository 


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

Я использую Moq, и я запутался, как правильно работать с несколькими интерфейсами.

+0

Есть ли связь между '' IUserRepository' и IGenericRepository'? Требуется ли, чтобы объект, реализующий «IUserRepository», также реализовал «IGenericRepository »? –

+0

Как выглядит производственный код, который вы пытаетесь проверить? –

ответ

3

Вы не издеваетесь классы, вы издеваетесь интерфейсы. В вашем случае у вас может быть два макета - тот, который насмехается IUserRepository и тот, который насмехается IGenericRepository<User>. Они не обязательно должны быть одним и тем же объектом - если они должны быть одним и тем же объектом, то это может быть дефект дизайна.

+0

+1 для краткости. Мы не заботимся о классе, поскольку мы явно НЕ запускаем его код, просто макет. –

+0

Чтобы уточнить, вы можете, конечно, высмеять класс (особенно абстрактный класс), по крайней мере, любые виртуальные методы/свойства. Я согласен с вашей точкой, но вы бы издевались над (public/protected/internal) * interface * класса. – Serguei

+3

Одним из преимуществ интерфейсов является то, что один объект может реализовывать множество интерфейсов. Это не является изначально недостатком дизайна. Я мог бы реализовать IDisposable и IEnumerable. Он также часто используется с [мечеными интерфейсами] (https://en.wikipedia.org/wiki/Marker_interface_pattern), и в этом случае для некоторого тестирования необходимо издеваться над объектом, который реализует несколько интерфейсов. Например, я использую интерфейс IEncrypted для пометки DTO, который должен быть зашифрован после сериализации, но все DTO должны реализовать наш интерфейс IMessage (который на самом деле имеет свойства для него для транзита). – blockloop

3

Если я правильно понял вопрос правильно, вы хотите иметь один макет экземпляра UserRepository, и для целей испытания, настройки вызовов методов как из интерфейса IGenericRepository<TEntity> и интерфейса IUserRepository.

Вы можете реализовать несколько интерфейсов с одним макетом, например, как это:

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

Однако, как D Стэнли отметил необходимость сделать это, вероятно, признак того, что есть ошибка в вашем дизайне.

22

Существует механизм, встроенный в Moq для работы с несколькими интерфейсами.

Скажет, у нас есть интерфейс IFoo и реализация того же Foo. У нас также есть ClientOne, который использует IFoo.

тогда у нас есть интерфейс IFooBar: IFoo, реализация FooBar: Foo, IFooBar и ClientTwo, который использует IFooBar.

При создании теста от конца до конца для системы мы имеем IFooBar, ClientOne и ClientTwo. Как <>() функция позволяет использовать Mock <IFooBar> как 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

Посмотрите на https://github.com/Moq/moq4/wiki/Quickstart

Расширенные функциональные возможности

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

Добавляя к этому ответу, если вам нужно передать макет методу, который принимает IFoo, вы также можете переключать IFoo и IDisposable, то есть: var disposableFoo = new Mock (); var foo = disposableFoo.As (); – suhendri

+0

Это определенно должен быть принятым ответом. –

Смежные вопросы