如何用具体类型模拟ITable

时间:2014-08-14 16:50:18

标签: c# linq unit-testing moq

我正在为使用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);

论坛是自动生成的课程。我想设置Table的论坛,这样当我在ContextWrapper上做一个GetTable时,我得到了论坛表。我不知道Table.Attach是否会将论坛附加到桌面上。当我运行测试时,它说

  

'类型为mock必须是接口或抽象类或非密封   类。

我弄错了吗?

2 个答案:

答案 0 :(得分:3)

我看到你正在尝试做的几个问题。

  1. 发布的代码无法编译

    IDataContextWrapper.GetTable会返回Table<T>,因此您无法将其设置为返回模拟的ITable<T>ITable 一个Table,反之亦然。这引出了我的下一点:

  2. IDataContextWrapper.GetTable应该返回ITable<T>,而不是Table<T>

    这将让你模拟返回结果,因为Table<T>sealed(Moq不能模拟密封的类,这可能是你得到你提到的错误的原因)。它也是program to interfaces而不是混凝土的好设计。

  3. 您不应期望拨打Attach来执行任何操作

    你试图在一个模拟界面上调用一个方法,就像你期望它表现得好像已经实现了它一样。 mock的方法只会按照你告诉他们的方式执行,因此在这种情况下(使用Loose behavior)它将对该调用不执行任何操作。相反,你应该设置你希望表格做什么,但这导致我:

  4. 您无法设置对Single的调用,因为它是一种扩展方法

    Moq doesn't support setting up extension methods,因为它们是静态方法。但是,您可以设置GetEnumerator的呼叫,这是Single呼叫的呼叫。你需要mock the IQueryable<T> members,因为那是Single真正要达到的目的。

  5. 因此,在解决上述第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);
    }
    

    请注意,这是一个很好的,不是很好的测试。您可以通过调用Single来取代对First的调用,但它仍会通过,但显然一般都是错误的。您至少应该添加此测试的否定值,即如果Id不匹配则不返回任何对象。

答案 1 :(得分:0)

尝试这样的事情

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