C# 使用带有MOQ的同步方法测试EF异步方法

C# 使用带有MOQ的同步方法测试EF异步方法,c#,entity-framework,unit-testing,asynchronous,moq,C#,Entity Framework,Unit Testing,Asynchronous,Moq,我有这个方法: 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 =

我有这个方法:

    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();
    }
}
这一行:错误

没有它:一次成功的测试

如何解决此问题?

.AsQueryable()
生成的
EnumerableQuery
类没有实现
IDbAsyncQueryProvider
,但通过实现可以轻松扩展
EnumerableQuery
。创建其中一个,而不是调用
.AsQueryable()
来包装您的集合。我在下面有一个实现,它将它进一步扩展到
IDbSet
,但您可能不需要走那么远

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);
    }
}
类存根集:EnumerableQuery、IDbSet、IDBSyncQueryProvider
T:在哪里上课
{
公共存根集(IEnumerable集合):基(集合)
{
本地=新的可观测采集(采集);
}
公共可观测集合本地{get;私有集;}
public T Find(参数对象[]键值)
{
抛出新的NotImplementedException();
}
公共T添加(T实体)
{
本地。添加(实体);
返回实体;
}
公共T删除(T实体)
{
本地。移除(实体);
返回实体;
}
公共T附加(T实体)
{
返回添加(实体);
}
公共文件不能创建()
{
抛出新的NotImplementedException();
}
公共TDerivedEntity Create(),其中TDerivedEntity:class,T
{
抛出新的NotImplementedException();
}
公共void DeleteObject(T实体)
{
抛出新的NotImplementedException();
}
公共无效分离(T实体)
{
抛出新的NotImplementedException();
}        
异步任务IDBSyncQueryProvider.ExecuteAsync(表达式表达式,CancellationToken CancellationToken)
{
返回((IQueryProvider)this.Execute(表达式);
}
异步任务IDBSyncQueryProvider.ExecuteAsync(表达式表达式,CancellationToken CancellationToken)
{
返回((IQueryProvider)this.Execute(表达式);
}
}

也许这篇文章来得有点晚,但无论如何:)

我建议您使用此库:

以下是GitHub项目:

请记住将所有的数据库集设置为虚拟

如果要使用FindAsync,则需要在“SetupData”方法中提供一个“Func”委托

例:

在这里,您可以看到此库如何模拟FindAsync:


希望这对别人有用

这很可能是mocking框架的问题,与EF或测试无关。我已经更新了这个问题,以反映这篇MSDN文章似乎是你的来源:发布它肯定会帮助其他人。
context.Setup(x => x.Users.Remove(user)).Returns(user).Verifiable();
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);
    }
}
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?);
}