DbSet mock,第二次调用ToList时没有结果

时间:2014-05-04 19:54:44

标签: c# .net entity-framework unit-testing ado.net

我正在尝试模拟DbContext和DbSet。这适用于我之前的单元测试,但是当我的代码第二次在DbSet上调用ToList方法时出现问题。

首先,dbSet.ToList()返回模拟结果。 第二个返回0个元素;

       var queryableData = new List<string>{ "a", "b", "c" }.AsQueryable();

        var mockDbSet = new Mock<DbSet<string>>();
        var q = mockDbSet.As<IQueryable<string>>();
        q.Setup(m => m.Provider).Returns(queryableData.Provider);
        q.Setup(m => m.Expression).Returns(queryableData.Expression);
        q.Setup(m => m.ElementType).Returns(queryableData.ElementType);
        q.Setup(m => m.GetEnumerator()).Returns(queryableData.GetEnumerator());

        DbSet<string> dbset = mockDbSet.Object;
        IQueryable<string> query = dbset;

        //RESULTS: abc
        var a1 = dbset.ToList();
        foreach (var a in a1)
            Console.Write(a);

        //NO RESULTS
        var a2 = dbset.ToList();
        foreach (var a in a2)
            Console.Write(a);

4 个答案:

答案 0 :(得分:39)

每次调用GetEnumerator时,都会返回相同的枚举器实例。当它枚举一次时,它就完成了,EF没有调用它的Reset方法,而是要求新的枚举器。

但是你会返回那个刚刚产生了所有元素而不再产生的元素。

相反,返回一个返回枚举器的函数,每次请求它时都会返回一个新的枚举器。

 q.Setup(m => m.GetEnumerator()).Returns( () => queryableData.GetEnumerator() );

答案 1 :(得分:2)

我只想添加到Wiktor Zychla的回答我的小部分。如果有人正在寻找此模拟的异步版本(来自本教程:http://msdn.microsoft.com/en-us/data/dn314429.aspx#async),那么这是我对TestDbAsyncEnumerator<T>类的修改:

internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
    private readonly IEnumerator<T> _inner;

    public TestDbAsyncEnumerator(IEnumerator<T> inner)
    {
        _inner = inner;
    }

    public TestDbAsyncEnumerator(Func<IEnumerator<T>> valueFunction)
    {
        _inner = valueFunction();
    }

    public void Dispose()
    {
        _inner.Dispose();
    }

    public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(_inner.MoveNext());
    }

    public T Current
    {
        get { return _inner.Current; }
    }

    object IDbAsyncEnumerator.Current
    {
        get { return Current; }
    }
}

然后像Wiktor建议你必须使用委托设置它,所以在异步的情况下它会是这样的:

mockSet.As<IDbAsyncEnumerable<Blog>>()
            .Setup(m => m.GetAsyncEnumerator())
            .Returns(new TestDbAsyncEnumerator<Blog>(() => data.GetEnumerator()));

如果有人想要获取相关内容,请点击此处:https://github.com/kelostrada/EntityFrameworkWithMock.Test

答案 2 :(得分:2)

我无法评论Wiktor的帖子,因为我没有足够的声誉,但我想在Wiktor的答案中添加一些额外内容。

代码

mockSet.Setup((m => m.GetEnumerator()).Returns(() => data.GetEnumerator())

如果你在示例中给它一个零参数lambda(至少我正在使用的版本),它将失败。 传递一个从未使用的参数将允许它满足签名要求。

mockSet.Setup((m => m.GetEnumerator()).Returns(x => data.GetEnumerator())

然而,将编译,并按预期工作。

这也适用于Kelu的答案(尽管他已将lambda添加到了错误的位置。

mockSet.As<IDbAsyncEnumerable<Blog>>()
        .Setup(m => m.GetAsyncEnumerator())
        .Returns(x => new TestDbAsyncEnumerator<Blog>(data.GetEnumerator()));

我不想在这里挑剔答案,我只是加入,因为我自己犯了这些错误:)

答案 3 :(得分:0)

如果你把&#34; Where&#34;在.ToList()调用之前,数据应保持存在。

var a1 = dbset.Where(m => m != null).ToList();

var a2 = dbset.Where(m => m != null).ToList();