我有这个方法:
public async Task DeleteUserAsync(Guid userId)
{
using (var context = this.contextFactory.Create())
{
var user = await context.Users.FirstOrDefaultAsync(x => x.Id.Equals(userId));
if (user == null)
{
throw new Exception("User doesn't exist");
}
context.Users.Remove(user);
await context.SaveChangesAsync();
}
}
我想测试一下。所以我创建了测试:
[TestMethod]
public async Task DeleteUsersSuccessfulCallTest()
{
// Arrange
var id = Guid.NewGuid();
var user = new User() { Id = id };
var context = new Mock<IDashboardContext>();
var usersDbSet = DbSetQueryMocking.GenericSetupAsyncQueryableMockInterfaceSet(new List<User> { user }.AsQueryable());
context.Setup(x => x.Users).Returns(usersDbSet.Object);
context.Setup(x => x.Users.Remove(user)).Returns(user).Verifiable();
context.Setup(x => x.SaveChangesAsync()).ReturnsAsync(1).Verifiable();
this.contextFactory.Setup(x => x.Create()).Returns(context.Object);
// Act
await this.userService.DeleteUserAsync(id);
context.VerifyAll();
}
}
我有这个方法来创建一个模拟集:
public static Mock<DbSet<T>> GenericSetupAsyncQueryableMockSet<T>(IQueryable<T> data) where T : class
{
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IDbAsyncEnumerable<T>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<T>(data.Provider));
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
return mockSet;
}
但是,由于我的DeleteUserAsync
包含异步扩展方法和标准同步方法,因此出现此错误消息:
System.InvalidOperationException:源IQueryable的提供程序不实现IDbAsyncQueryProvider。只有实现IDbAsyncQueryProvider的提供程序才能用于Entity Framework异步操作。有关详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=287068。
显然,如果我只是将DbSet<T>
与Queryable
设置为模拟,那么它会抛出相同的异常。
仅供参考:违规行为:
context.Setup(x => x.Users.Remove(user)).Returns(user).Verifiable();
使用此行:错误
没有它:成功的测试。
我该如何解决这个问题?
答案 0 :(得分:14)
由EnumerableQuery<T>
生成的.AsQueryable()
类未实现IDbAsyncQueryProvider
,但随着实施的扩展EnumerableQuery<T>
很容易。创建其中一个而不是调用.AsQueryable()
来包装您的集合。我在下面有一个实现,它进一步扩展到IDbSet<T>
,但你可能不需要那么远。
class StubSet<T> : EnumerableQuery<T>, IDbSet<T>, IDbAsyncQueryProvider
where T : class
{
public StubSet(IEnumerable<T> collection) : base(collection)
{
Local = new ObservableCollection<T>(collection);
}
public ObservableCollection<T> Local { get; private set; }
public T Find(params object[] keyValues)
{
throw new NotImplementedException();
}
public T Add(T entity)
{
Local.Add(entity);
return entity;
}
public T Remove(T entity)
{
Local.Remove(entity);
return entity;
}
public T Attach(T entity)
{
return Add(entity);
}
public T Create()
{
throw new NotImplementedException();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
throw new NotImplementedException();
}
public void DeleteObject(T entity)
{
throw new NotImplementedException();
}
public void Detach(T entity)
{
throw new NotImplementedException();
}
async Task<object> IDbAsyncQueryProvider.ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return ((IQueryProvider)this).Execute(expression);
}
async Task<TResult> IDbAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return ((IQueryProvider)this).Execute<TResult>(expression);
}
}
答案 1 :(得分:0)
也许这篇文章来得有点晚,但无论如何:)
我建议您使用此库: https://www.nuget.org/packages/EntityFrameworkTesting/
这是GitHub项目: https://github.com/scott-xu/EntityFramework.Testing
请记住将所有 DbSet虚拟化!
如果要使用 FindAsync ,则需要在“ SetupData”方法中提供“ Func”委托
例如:
var mockDbSetDisplays = new Mock<DbSet<DbEntity>>().SetupData(dataSourceDisplays, this.FindDisplay);
.....
private Display FindDisplay(object[] arg)
{
return this.dataSourceDisplays.FirstOrDefault(x=> x.DisplayId == arg[0] as int?);
}
在这里您可以看到该库如何模拟FindAsync:
希望这对某人有用!