nik*_*d23 7 entity-framework mocking entity-framework-6
通过利用MSDN 上的Testing with a Mocking Framework文章中的Testing with async queries部分,我已经能够创建许多成功通过测试.
这是我的测试代码,它使用NSubstitute进行模拟:
var dummyQueryable = locations.AsQueryable();
var mock = Substitute.For<DbSet<Location>, IDbAsyncEnumerable<Location>, IQueryable<Location>>();
((IDbAsyncEnumerable<Location>)mock).GetAsyncEnumerator().Returns(new TestDbAsyncEnumerator<Location>(dummyQueryable.GetEnumerator()));
((IQueryable<Location>)mock).Provider.Returns(new TestDbAsyncQueryProvider<Location>(dummyQueryable.Provider));
((IQueryable<Location>)mock).Expression.Returns(dummyQueryable.Expression);
((IQueryable<Location>)mock).ElementType.Returns(dummyQueryable.ElementType);
((IQueryable<Location>)mock).GetEnumerator().Returns(dummyQueryable.GetEnumerator());
sut.DataContext.Locations = mock;
var result = await sut.Index();
result.Should().BeView();
Run Code Online (Sandbox Code Playgroud)
sut.Index() 没有做太多,但它做了以下查询:
await DataContext.Locations
.GroupBy(l => l.Area)
.ToListAsync());
Run Code Online (Sandbox Code Playgroud)
这可以正常工作,直到我在查询中添加一个投影:
await DataContext.Locations
.GroupBy(l => l.Area)
.Select(l => new LocationsIndexVM{ Area = l.Key }) // added projection
.ToListAsync());
Run Code Online (Sandbox Code Playgroud)
这导致了这个例外:
System.InvalidOperationException
The source IQueryable doesn't implement IDbAsyncEnumerable<LocationsIndexVM>. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
at System.Data.Entity.QueryableExtensions.AsDbAsyncEnumerable(IQueryable`1 source)
at System.Data.Entity.QueryableExtensions.ToListAsync(IQueryable`1 source)
at Example.Web.Controllers.HomeController.<Index>d__0.MoveNext() in HomeController.cs: line 25
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Example.Test.Web.Controllers.HomeControllerShould.<TempTest>d__4.MoveNext() in HomeControllerShould.cs: line 71
Run Code Online (Sandbox Code Playgroud)
更新:我上传了一个简单的小解决方案,可以重现这个问题.
任何人都可以提供一个例子,说明单独测试同时async包含.Select()投影的查询所需的内容吗?
jef*_*ora 16
所以我做了一些挖掘,问题是TestDbAsyncEnumerable<T>暴露的方式IQueryProvider.关于推理的最佳猜测如下,以及下面的解决方案.
TestDbAsyncEnumerable<T>继承自EnumerableQuery<T>,继承自IQueryable<T>,并显式实现Provider此接口的属性:
IQueryProvider IQueryable.Provider { get ... }
Run Code Online (Sandbox Code Playgroud)
鉴于它是明确实现的,我假设LINQ内部在尝试获取之前显式地转换了类型Provider:
((IQueryable<T>)source).Provider.CreateQuery(...);
Run Code Online (Sandbox Code Playgroud)
我手边没有源代码(并且不会费心寻找),但我认为类型绑定规则对于显式实现是不同的; 基本上,Provider你的属性TestDbAsyncEnumerable<T>不被视为一个实现,IQueryable<T>.Provider因为一个明确的属性存在于链上,所以你TestDbAsyncQueryProvider<T>永远不会返回.
对此的修复是使TestDbAsyncEnumerable<T>继承IQueryable<T>和显式实现该Provider属性,如下所示(根据您链接的MSDN文章调整):
public class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression) : base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2910 次 |
| 最近记录: |