Test des méthodes asynchrones EF avec des méthodes de synchronisation 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 le tester. Alors je crée le test:

  [TestMethod] public async Task DeleteUsersSuccessfulCallTest() { // Arrange var id = Guid.NewGuid(); var user = new User() { Id = id }; var context = new Mock(); var usersDbSet = DbSetQueryMocking.GenericSetupAsyncQueryableMockInterfaceSet(new List { 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 me créer un jeu de simulation:

  public static Mock<DbSet> GenericSetupAsyncQueryableMockSet(IQueryable data) where T : class { var mockSet = new Mock<DbSet>(); mockSet.As<IDbAsyncEnumerable>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator(data.GetEnumerator())); mockSet.As<IQueryable>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider(data.Provider)); mockSet.As<IQueryable>().Setup(m => m.Expression).Returns(data.Expression); mockSet.As<IQueryable>().Setup(m => m.ElementType).Returns(data.ElementType); mockSet.As<IQueryable>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); return mockSet; } 

Cependant, parce que mon DeleteUserAsync contient des méthodes d’extension async et des méthodes de synchronisation standard, je reçois le message d’erreur suivant:

System.InvalidOperationException: le fournisseur de la source IQueryable n’implémente pas IDbAsyncQueryProvider. Seuls les fournisseurs qui implémentent IDbAsyncQueryProvider peuvent être utilisés pour les opérations asynchrones Entity Framework. Pour plus de détails, voir http://go.microsoft.com/fwlink/?LinkId=287068 .

Évidemment, si je viens de configurer le DbSet avec Queryable , alors la même exception sera lancée.

FYI: la ligne incriminée est:

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

Avec cette ligne: les erreurs

Sans cela: un test réussi.

Comment puis-je réparer ça?

    La EnumerableQuery produite par .AsQueryable() pas IDbAsyncQueryProvider mais il est facile d’étendre EnumerableQuery avec l’implémentation. Créez-en un au lieu d’appeler .AsQueryable() pour envelopper votre collection. J’ai une implémentation ci-dessous qui l’étend plus loin dans un IDbSet mais vous n’aurez peut-être pas besoin d’aller aussi loin.

     class StubSet : EnumerableQuery, IDbSet, IDbAsyncQueryProvider where T : class { public StubSet(IEnumerable collection) : base(collection) { Local = new ObservableCollection(collection); } public ObservableCollection 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() 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 IDbAsyncQueryProvider.ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return ((IQueryProvider)this).Execute(expression); } async Task IDbAsyncQueryProvider.ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return ((IQueryProvider)this).Execute(expression); } }