2014-12-15 8 views
12

ho questo metodo:test EF metodi asincroni con i metodi di sincronizzazione con 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 == null) 
      { 
       throw new Exception("User doesn't exist"); 
      } 

      context.Users.Remove(user); 

      await context.SaveChangesAsync(); 
     } 
    } 

voglio provarlo. Così ho creato il 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(); 
    } 
} 

ho avuto questo metodo per creare me un set finto:

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

Tuttavia, perché la mia DeleteUserAsync contiene i metodi di estensione asincroni e metodi di sincronizzazione standard, ricevo questo messaggio di errore :

System.InvalidOperationException: il provider per l'origine IQueryable non implementa IDbAsyncQueryProvider. Solo i provider che implementano IDbAsyncQueryProvider possono essere utilizzati per le operazioni asincrone di Entity Framework. Per maggiori dettagli vedi http://go.microsoft.com/fwlink/?LinkId=287068.

Ovviamente se ho appena impostato il DbSet<T> con Queryable deriso fuori allora si getterà la stessa eccezione.

proposito: la riga all'origine è:

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

Con questa linea: errori

Senza di esso: una prova di successo.

Come posso risolvere questo problema?

+0

Questo è probabilmente il problema della struttura di derisione e non ha nulla a che fare con EF o test. – Euphoric

+0

Ho aggiornato la domanda per riflettere questo –

+0

https://msdn.microsoft.com/en-us/data/dn314429#async – Ali

risposta

11

La classe EnumerableQuery<T> che è prodotta dal .AsQueryable() non implementa IDbAsyncQueryProvider ma è facile estendere EnumerableQuery<T> con l'implementazione. Crea uno di questi invece di chiamare .AsQueryable() per racchiudere la tua raccolta. Ho un'implementazione di seguito che estende ulteriormente in un IDbSet<T> ma potrebbe non essere necessario andare così lontano.

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); 
    } 
} 
+6

Questo articolo MSDN sembra essere la vostra fonte: https://msdn.microsoft.com/en -us/library/dn314429.aspx Pubblicarlo aiuterà sicuramente gli altri. –