以下是由于 .ToListAsync() 不受内存数据库集支持而在 .ShouldNotThrow() 上失败的那种测试(我没有方便的确切措辞,但你明白了)。万一它很重要,我正在尝试模拟 Entity Framework 版本提供的数据库集。 6.1.3:
[TestFixture]
public class Tests
{
private SomeRepository _repository;
private Mock<DbSet<SomeEntity>> _mockDbSet;
private Mock<IApplicationDbContext> _mockAppDbContext;
[OneTimeSetUp]
public void TestFixtureSetUp()
{
_mockDbSet = new Mock<DbSet<SomeEntity>>();
_mockAppDbContext = new Mock<IApplicationDbContext>();
_mockAppDbContext.SetupGet(c => c.Gigs).Returns(_mockGigsDbSet.Object);
_repository = new SomeRepository(_mockAppDbContext.Object);
}
[Test]
public void Test()
{
// Setup
var results = (IEnumerable<SomeEntity>) null;
var singleEntity = new SomeEntity {Id = "1"};
_mockDbSet.SetSource(new List<SomeEntity> { singleEntity });
// Act
var action = new Func<Task>(async () =>
{
results = await _repository.GetMultipleAsync(); //this ends up calling "await mockDbSet.ToListAsync().ConfigureAwait(false)" internally
});
// Verify
action.ShouldNotThrow(); //an exception is thrown about .ToListAsync() not being supported by in-memory dbsets or something to that effect
results.Should().BeEmpty();
}
}
如果同步使用 .ToList() 代替基于异步的 .ToListAsync(),上述测试将按预期工作。在实际的 asp.net 中使用时,存储库也可以正常工作。
那么在这些单元测试中模拟 .ToListAsync() 的 dbset 以工作的正确方法是什么?
P.S.:我一直在进行单元测试的项目可以在这里找到:
https://bitbucket.org/dsidirop/gighub
由于 .ToListAsync() 而失败的单元测试标有注释“暂时失败”。
最佳答案
感谢 Bradford Dillon 提供了正确的答案:
https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx
为存储库的异步方法创建单元测试的正确方法是首先创建这些实用程序模型类:
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace TestingDemo
{
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal TestDbAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new TestDbAsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestDbAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute(expression));
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public void Dispose()
{
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
public T Current
{
get { return _inner.Current; }
}
object IDbAsyncEnumerator.Current
{
get { return Current; }
}
}
}
然后像这样使用它们:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Threading.Tasks;
namespace TestingDemo
{
[TestClass]
public class AsyncQueryTests
{
[TestMethod]
public async Task GetAllBlogsAsync_orders_by_name()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IDbAsyncEnumerable<Blog>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<Blog>(data.GetEnumerator()));
mockSet.As<IQueryable<Blog>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<Blog>(data.Provider));
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
var blogs = await service.GetAllBlogsAsync();
Assert.AreEqual(3, blogs.Count);
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("ZZZ", blogs[2].Name);
}
}
}
关于asp.net - 使用内存对 .ToListAsync() 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41324783/