Tester les méthodes EF async avec les méthodes sync avec MOQ

j'ai cette méthode:

    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();
        }
    }

je veux faire un test. Je crée donc le test:

    [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();
    }
}

j'ai cette méthode pour créer moi un simulacre de jeu:

    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;
    }

Cependant, parce que mon DeleteUserAsync contient async les méthodes d'extension standard et de modes de synchronisation j'obtiens ce message d'erreur:

Système.InvalidOperationException: le fournisseur de la source IQueryable n'implémente pas IDbAsyncQueryProvider. Seulement les fournisseurs qui mettent en œuvre IDbAsyncQueryProvider peut être utilisé pour les opérations asynchrones du framework Entity. Pour plus de détails, voirhttp://go.microsoft.com/fwlink/?LinkId=287068.

Évidemment, si je viens de mettre en place le DbSet<T>Queryable moqué de s'en sortir, alors qu'il va jeter la même exception.

FYI: la ligne incriminée est:

context.Setup(x => x.Users.Remove(user)).Returns(user).Verifiable();

avec cette ligne: errors

Sans elle: la réussite d'un test.

Comment puis-je résoudre ce problème?

15
demandé sur Callum Linington 2014-12-15 15:06:06

1 réponses

EnumerableQuery<T> classe qui est produite par .AsQueryable() n'implémente pas IDbAsyncQueryProvider mais c'est facile à étendre EnumerableQuery<T> avec l'implémentation. Créer une de ces au lieu de l'appeler IDbSet<T> mais vous ne pouvez pas besoin d'aller aussi loin.

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);
    }
}
13
répondu Tim Rogers 2014-12-15 13:56:13