[英]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.