如何使用Entity Framework Core模拟异步存储库

编程入门 行业动态 更新时间:2024-10-08 02:19:40
本文介绍了如何使用Entity Framework Core模拟异步存储库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在尝试为一个调用异步存储库的类创建一个单元测试.我正在使用ASP.NET Core和Entity Framework Core.我的通用存储库看起来像这样.

I'm trying to create a unit test for a class that calls into an async repository. I'm using ASP.NET Core and Entity Framework Core. My generic repository looks like this.

public class EntityRepository<TEntity> : IEntityRepository<TEntity> where TEntity : class { private readonly SaasDispatcherDbContext _dbContext; private readonly DbSet<TEntity> _dbSet; public EntityRepository(SaasDispatcherDbContext dbContext) { _dbContext = dbContext; _dbSet = dbContext.Set<TEntity>(); } public virtual IQueryable<TEntity> GetAll() { return _dbSet; } public virtual async Task<TEntity> FindByIdAsync(int id) { return await _dbSet.FindAsync(id); } public virtual IQueryable<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate) { return _dbSet.Where(predicate); } public virtual void Add(TEntity entity) { _dbSet.Add(entity); } public virtual void Delete(TEntity entity) { _dbSet.Remove(entity); } public virtual void Update(TEntity entity) { _dbContext.Entry(entity).State = EntityState.Modified; } public virtual async Task SaveChangesAsync() { await _dbContext.SaveChangesAsync(); } }

然后我有一个服务类,该服务类在存储库的实例上调用FindBy和FirstOrDefaultAsync:

Then I have a service class that calls FindBy and FirstOrDefaultAsync on an instance of the repository:

public async Task<Uri> GetCompanyProductURLAsync(Guid externalCompanyID, string productCode, Guid loginToken) { CompanyProductUrl companyProductUrl = await _Repository.FindBy(u => u.Company.ExternalCompanyID == externalCompanyID && u.Product.Code == productCode.Trim()).FirstOrDefaultAsync(); if (companyProductUrl == null) { return null; } var builder = new UriBuilder(companyProductUrl.Url); builder.Query = $"-s{loginToken.ToString()}"; return builder.Uri; }

我正在尝试在下面的测试中模拟存储库调用:

I'm trying to mock the repository call in my test below:

[Fact] public async Task GetCompanyProductURLAsync_ReturnsNullForInvalidCompanyProduct() { var companyProducts = Enumerable.Empty<CompanyProductUrl>().AsQueryable(); var mockRepository = new Mock<IEntityRepository<CompanyProductUrl>>(); mockRepository.Setup(r => r.FindBy(It.IsAny<Expression<Func<CompanyProductUrl, bool>>>())).Returns(companyProducts); var service = new CompanyProductService(mockRepository.Object); var result = await service.GetCompanyProductURLAsync(Guid.NewGuid(), "wot", Guid.NewGuid()); Assert.Null(result); }

但是,当测试执行对存储库的调用时,出现以下错误:

However, when the test executes the call to the repository, I get the following error:

The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations.

如何正确模拟存储库以使其正常工作?

How can I properly mock the repository to get this to work?

推荐答案

感谢@Nkosi指出了指向我的链接,并提供了在EF 6中执行相同操作的示例: msdn.microsoft/en-us/library/dn314429.aspx .这与EF Core并不完全一样,但是我能够从EF Core开始并进行修改以使其正常工作.下面是我创建的用于模拟" IAsyncQueryProvider的测试类:

Thanks to @Nkosi for pointing me to a link with an example of doing the same thing in EF 6: msdn.microsoft/en-us/library/dn314429.aspx. This didn't work exactly as-is with EF Core, but I was able to start with it and make modifications to get it working. Below are the test classes that I created to "mock" IAsyncQueryProvider:

internal class TestAsyncQueryProvider<TEntity> : IAsyncQueryProvider { private readonly IQueryProvider _inner; internal TestAsyncQueryProvider(IQueryProvider inner) { _inner = inner; } public IQueryable CreateQuery(Expression expression) { return new TestAsyncEnumerable<TEntity>(expression); } public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new TestAsyncEnumerable<TElement>(expression); } public object Execute(Expression expression) { return _inner.Execute(expression); } public TResult Execute<TResult>(Expression expression) { return _inner.Execute<TResult>(expression); } public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression) { return new TestAsyncEnumerable<TResult>(expression); } public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute<TResult>(expression)); } } internal class TestAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T> { public TestAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { } public TestAsyncEnumerable(Expression expression) : base(expression) { } public IAsyncEnumerator<T> GetEnumerator() { return new TestAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); } IQueryProvider IQueryable.Provider { get { return new TestAsyncQueryProvider<T>(this); } } } internal class TestAsyncEnumerator<T> : IAsyncEnumerator<T> { private readonly IEnumerator<T> _inner; public TestAsyncEnumerator(IEnumerator<T> inner) { _inner = inner; } public void Dispose() { _inner.Dispose(); } public T Current { get { return _inner.Current; } } public Task<bool> MoveNext(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); } }

这是我更新的使用以下类的测试用例:

And here is my updated test case that uses these classes:

[Fact] public async Task GetCompanyProductURLAsync_ReturnsNullForInvalidCompanyProduct() { var companyProducts = Enumerable.Empty<CompanyProductUrl>().AsQueryable(); var mockSet = new Mock<DbSet<CompanyProductUrl>>(); mockSet.As<IAsyncEnumerable<CompanyProductUrl>>() .Setup(m => m.GetEnumerator()) .Returns(new TestAsyncEnumerator<CompanyProductUrl>(companyProducts.GetEnumerator())); mockSet.As<IQueryable<CompanyProductUrl>>() .Setup(m => m.Provider) .Returns(new TestAsyncQueryProvider<CompanyProductUrl>(companyProducts.Provider)); mockSet.As<IQueryable<CompanyProductUrl>>().Setup(m => m.Expression).Returns(companyProducts.Expression); mockSet.As<IQueryable<CompanyProductUrl>>().Setup(m => m.ElementType).Returns(companyProducts.ElementType); mockSet.As<IQueryable<CompanyProductUrl>>().Setup(m => m.GetEnumerator()).Returns(() => companyProducts.GetEnumerator()); var contextOptions = new DbContextOptions<SaasDispatcherDbContext>(); var mockContext = new Mock<SaasDispatcherDbContext>(contextOptions); mockContext.Setup(c => c.Set<CompanyProductUrl>()).Returns(mockSet.Object); var entityRepository = new EntityRepository<CompanyProductUrl>(mockContext.Object); var service = new CompanyProductService(entityRepository); var result = await service.GetCompanyProductURLAsync(Guid.NewGuid(), "wot", Guid.NewGuid()); Assert.Null(result); }

更多推荐

如何使用Entity Framework Core模拟异步存储库

本文发布于:2023-10-26 06:42:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1529380.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:如何使用   Entity   Core   Framework

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!