[英]how to mock ITable with a concrete type
我正在為使用System.Data.Linq DataConext對象從數據庫獲取實體表的存儲庫編寫單元測試。 這是代碼:
public class ForumRepository : IForumRepository
{
protected Table<Forum> DataTable;
IDataContextWrapper DataContext;
public ForumRepository(IDataContextWrapper DataContext)
{
DataTable = DataContext.GetTable<Forum>();
}
public Forum GetForumById(int id)
{
try
{
return DataTable.Single(f => f.tblForumID.Equals(id));
}
catch(Exception e)
{
return null;
}
}
這是包裝器實現:
public class DataContextWrapper<T> : IDataContextWrapper where T : EpixForumDataContext, new()
{
private readonly T db;
public DataContextWrapper()
{
var t = typeof(T);
db = (T)Activator.CreateInstance(t);
}
public DataContextWrapper(string connectionString)
{
var t = typeof(T);
db = (T)Activator.CreateInstance(t, connectionString);
}
public Table<TableName> GetTable<TableName>() where TableName : class
{
return (Table<TableName>)db.GetTable(typeof(TableName));
}
我想測試存儲庫方法。
public class UnitTest1
{
[TestMethod]
public void Can_Get_Forum_ById()
{
//arrange
Forum dummyForum = new Forum() { tblForumID = 1};
Mock<ITable<Forum>> tableMock = new Mock<ITable<Forum>>();
tableMock.Object.Attach(dummyForum);
Mock<IDataContextWrapper> mock = new Mock<IDataContextWrapper>();
mock.Setup(m => m.GetTable<Forum>()).Returns(tableMock.Object) ;
//act
ForumRepository repos = new ForumRepository(mock.Object);
Forum resultForum = repos.GetForumById(1);
//assert
Assert.AreEqual(resultForum.tblForumID, 1);
Forum是一個自動生成的類。 我想設置Table的論壇,這樣當我在ContextWrapper上做一個GetTable時,我得到了論壇表。 我不知道Table.Attach是否會將論壇附加到桌面上。 當我運行測試時,它說
'類型為mock必須是接口或抽象類或非密封類'。
我弄錯了嗎?
我看到你正在嘗試做的幾個問題。
發布的代碼無法編譯
IDataContextWrapper.GetTable
返回Table<T>
,因此您無法將其設置為返回ITable<T>
。 ITable
不是 Table
,而是另一種方式。 這引出了我的下一點:
IDataContextWrapper.GetTable
應返回ITable<T>
,而不是Table<T>
。
這將讓你模擬返回結果,因為Table<T>
是密封的 (Moq不能模擬密封類,這可能是你得到你提到的錯誤的原因)。 編程接口而不是混凝土也是很好的設計。
你不應該期望對Attach
的調用做任何事情
你試圖在一個模擬的界面上調用一個方法,就像你期望它表現得像某個東西已經實現它一樣。 mock的方法只會按照你告訴他們的方式執行,因此在這種情況下(使用Loose行為 )它將不會對該調用執行任何操作。 相反,你應該設置你希望表格做什么,但這導致我:
您無法將調用設置為Single
因為它是一種擴展方法
Moq 不支持設置擴展方法 ,因為它們是靜態方法。 但是,您可以設置對GetEnumerator
的調用,這是Single
調用的。 你需要模擬IQueryable<T>
成員 ,因為那是Single
真正要擊中的。
因此,在解決上述第1點之后,您的測試最終應如下所示:
[Test]
public void Can_Get_Forum_ById()
{
// arrange
Forum dummyForum = new Forum { tblForumID = 1 };
IQueryable<Forum> forums = new List<Forum> { dummyForum }.AsQueryable();
Mock<ITable<Forum>> tableMock = new Mock<ITable<Forum>>();
tableMock.Setup(p => p.GetEnumerator()).Returns(forums.GetEnumerator());
tableMock.Setup(r => r.Provider).Returns(forums.Provider);
tableMock.Setup(r => r.ElementType).Returns(forums.ElementType);
tableMock.Setup(r => r.Expression).Returns(forums.Expression);
Mock<IDataContextWrapper> mock = new Mock<IDataContextWrapper>();
mock.Setup(m => m.GetTable<Forum>()).Returns(tableMock.Object);
// act
ForumRepository repos = new ForumRepository(mock.Object);
Forum resultForum = repos.GetForumById(1);
// assert
Assert.AreEqual(resultForum.tblForumID, 1);
}
請注意,這是一個很好的,不是很好的測試。 您可以通過調用First
替換對Single
的調用,它仍會通過,但顯然通常是錯誤的。 您至少應該添加此測試的否定,即如果Id不匹配則不返回任何對象。
嘗試這樣的事情
Forum dummyForum = new Forum() { tblForumID = 1};
Mock<ITable> tableMock = new Mock<ITable>();
tableMock.Object.Attach(dummyForum);
Mock<IDataContextWrapper> contextMock = new Mock<IDataContextWrapper>();
contextMock .Setup(m => m.GetTable<Forum>()).Returns((ITable<Forum>)tableMock.Object) ;
//act
ForumRepository repos = new ForumRepository(mock.Object);
Forum resultForum = repos.GetForumById(1);
//assert
Assert.AreEqual(resultForum.tblForumID, 1);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.