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

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

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

var queryableData = new List{ "a", "b", "c" }.AsQueryable(); var mockDbSet = new Mock<DbSet>(); var q = mockDbSet.As<IQueryable>(); 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 dbset = mockDbSet.Object; IQueryable 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); 

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

但是你返回的只是已经产生所有元素并且不再产生的那个。

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

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

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

 internal class TestDbAsyncEnumerator : IDbAsyncEnumerator { private readonly IEnumerator _inner; public TestDbAsyncEnumerator(IEnumerator inner) { _inner = inner; } public TestDbAsyncEnumerator(Func> valueFunction) { _inner = valueFunction(); } public void Dispose() { _inner.Dispose(); } public Task MoveNextAsync(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); } public T Current { get { return _inner.Current; } } object IDbAsyncEnumerator.Current { get { return Current; } } } 

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

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

如果有人想要源代码那么你去吧: https : //github.com/kelostrada/EntityFrameworkWithMock.Test

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

代码

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

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

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

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

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

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

不试图在这里挑剔答案,我只是补充因为我自己犯了这些错误:)

如果在.ToList()调用之前放置“Where”子句,则数据应保持存在。

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