2014-12-15 15 views
12

У меня есть этот метод:Тестирование EF методы асинхронных с методами синхронизации с 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(); 
     } 
    } 

Я хочу, чтобы проверить его. Поэтому я создаю тест:

[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. Только асинхронные операции Entity Framework могут использоваться только провайдерами, которые реализуют IDbAsyncQueryProvider. Для получения дополнительной информации см. http://go.microsoft.com/fwlink/?LinkId=287068.

Очевидно, что если я просто настроить DbSet<T> с Queryable издевались из тогда он будет бросать же исключение.

FYI: виновный линия:

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

С этой линией: ошибки

Без него: успешный тест.

Как исправить это?

+0

Это, скорее всего, проблема издевательской структуры и не имеет ничего общего с EF или тестированием. – Euphoric

+0

Я обновил вопрос, чтобы отразить это –

+0

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

ответ

11

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

Эта статья MSDN кажется вашим источником: https://msdn.microsoft.com/ru -us/library/dn314429.aspx Проводка, безусловно, поможет другим. –

Смежные вопросы