2013-04-04 83 views
29

如何嘲笑下面的類的類:如何嘲笑實現多個接口

UserRepository : GenericRepository<User>, IUserRepository 


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

我用的起訂量,和我很困惑如何正確處理多個接口。

+0

有'IUserRepository'和'IGenericRepository'之間的關係?是否需要實現'IUserRepository'的對象也實現'IGenericRepository '? – 2013-04-04 20:06:14

+0

你試圖測試的產品代碼是什麼樣的? – 2013-04-04 20:07:20

回答

3

你不要模擬,你模擬接口。在你的情況下,你可能會有兩個嘲笑 - 一個嘲笑IUserRepository,另一個嘲笑IGenericRepository<User>。它們不一定是同一個對象 - 如果它們必須是同一個對象,那麼它可能是一個設計缺陷。

+0

+1簡潔。我們不關心這個類,因爲我們明確地不運行它的代碼,只是模擬。 – 2013-04-04 20:09:15

+0

爲了澄清,你當然可以嘲笑一個類(特別是抽象類),至少是任何虛擬方法/屬性。不過,我同意你的觀點,你會嘲笑班級的(公共/保護/內部)*界面。 – Serguei 2013-04-04 20:14:22

+3

接口的好處之一是一個對象可以實現許多接口。這不是固有的設計缺陷。我可以實現IDisposable和IEnumerable。它也經常與[標記接口](https://en.wikipedia.org/wiki/Marker_interface_pattern)一起使用,在這種情況下,模擬實現多個接口的對象對於某些測試是必需的。例如,我使用IEncrypted接口來標記必須在序列化後加密的DTO,但所有的DTO都必須實現我們的IMessage接口(實際上它具有用於傳輸的屬性)。 – blockloop 2016-03-12 05:07:02

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。我們也有使用IFoo的ClientOne

然後我們有一個接口IFooBar:IFoo的,實現FooBar的:美孚,IFooBarClientTwo使用IFooBar。

當創建一個終端到終端的測試系統,我們有一個IFooBarClientOneClientTwo。所述作爲<>()函數允許我們使用模擬<IFooBar>作爲模擬<的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 2016-09-27 21:06:31

+0

這絕對應該是被接受的答案。 – 2017-06-26 07:14:25