模拟数据库集<;T>;内联
本文关键字:gt 内联 lt 数据库 模拟 | 更新日期: 2023-09-27 18:27:19
我使用.NET4.5
、EF6
和Moq
进行单元测试。我正在尝试模拟一些Db数据进行测试。我有一个例子,通过将mockset声明为变量,然后使用mock来实现这一点。
public static class TestExtensionMethods
{
public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
return dbSet.Object;
}
}
我正在使用它作为ATM的扩展方法:mockedDbContext.Journey = new List<Journey> { }.AsDbSet();
有没有一种方法可以将其全部声明为一行(这样我就可以将数据库集作为TestCaseData
传递,而无需编写扩展方法)。我试着跟随
var mockedDbContext = new Mock<OnlineLegal>();
mockedDbContext.Setup(o => o.Journey).Returns(() => (DbSet<Journey>)(new List<Journey> { new Journey { SessionId = sessionId, ConveyancingAnswer = new Collection<ConveyancingAnswer>()} }.AsEnumerable()));
但它被System.InvalidCastException : Unable to cast object of type 'System.Collections.Generic.List1[Saga.Services.Legal.Website.Journey]' to type 'System.Data.Entity.DbSet1[Saga.Services.Legal.Website.Journey]'.
炸毁了
如何在线模拟DbSet<T>
编辑:关于复制,标记我问题的人除了标题之外没有花更多的精力阅读,今年将从圣诞老人那里得到沙林毒气。
在这种情况下,使用您定义的方法从列表创建DbSet,其中T是类型Journey
,并且假设o.Journey
是类型DbSet<Journey>
。
private DbSet<T> ToDbSet<T>(List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
return dbSet.Object;
}
private void SomeOtherMethod()
{
var journey = new Journey
{
SessionId = sessionId,
ConveyancingAnswer = new Collection<ConveyancingAnswer>()
};
var journeys = new List<Journey> { journey };
mockedDbContext.Setup(o => o.Journey)
.Returns(() => ToDbSet<Journey>(journeys));
}
如果你想把它放在一行上,那么它是可能的,但在可读性方面不一定是可取的。
我昨天已经花了几个小时讨论这个话题。@sileknarf的注释是正确的,但并不完整。异步方法将给出一个异常。我昨天在网上搜索了和anwser,并找到了这个扩展方法作为解决方案。它适用于dotnet 3.1。由于对接口进行了一些更改,以前的版本可能需要不同的实现。希望它对你们中的一些人有用。:)
public static class DbSet
{
public static DbSet<T> ToDbSet<T>(this List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IAsyncEnumerable<T>>()
.Setup(m => m.GetAsyncEnumerator(It.IsAny<CancellationToken>()))
.Returns(new MockAsyncEnumerator<T>(queryable.GetEnumerator()));
dbSet.As<IQueryable<T>>()
.Setup(m => m.Provider)
.Returns(new MockAsyncQueryProvider<T>(queryable.Provider));
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
return dbSet.Object;
}
private class MockAsyncQueryProvider<TEntity> : IAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal MockAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new MockAsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new MockAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
{
var expectedResultType = typeof(TResult).GetGenericArguments()[0];
var executionResult = typeof(IQueryProvider)
.GetMethod(name: nameof(IQueryProvider.Execute), genericParameterCount: 1, types: new[] { typeof(Expression) })
.MakeGenericMethod(expectedResultType)
.Invoke(this, new[] {expression});
return (TResult)typeof(Task)
.GetMethod(nameof(Task.FromResult))
?.MakeGenericMethod(expectedResultType)
.Invoke(null, new[] { executionResult });
}
}
private class MockAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
IQueryProvider IQueryable.Provider => new MockAsyncQueryProvider<T>(this);
public MockAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { }
public MockAsyncEnumerable(Expression expression) : base(expression) { }
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new MockAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
}
private class MockAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public T Current => _inner.Current;
public MockAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_inner.MoveNext());
}
public ValueTask DisposeAsync()
{
_inner.Dispose();
return new ValueTask();
}
}
}