Nek*_*ome 4 c# nhibernate unit-testing moq
我正在尝试使用最小起订量在 ASP.NET Core MVC 应用程序中创建单元测试。不幸的是,Nhibernate.ToListAsync()不支持 Linq IQueryabledataset 和 throw System.NotSupportedException: 'Source Provider must be a INhQueryProvider'。在这段代码中,我模拟了INhQueryProvider,但这还不够:
var entities = new List<RequestRole>
{
new RequestRole()
{
Id = 0,
RequestOperator = new RequestOperator() { Id = 1 }
},
new RequestRole()
{
Id = 1,
RequestOperator = new RequestOperator() { Id = 2 }
}
}
.AsQueryable();
// for ToListAsync Mock INhQueryProvider and set it into IQueryable
var queryableProviderMock = new Mock<INhQueryProvider>();
queryableProviderMock.Setup(x => x.ExecuteAsync<IEnumerable<RequestRole>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(entities);
var queryableMock = new Mock<IQueryable<RequestRole>>();
queryableMock.Setup(x => x.Provider).Returns(queryableProviderMock.Object);
queryableMock.Setup(x => x.Expression).Returns(entities.Expression);
queryableMock.Setup(x => x.GetEnumerator()).Returns(entities.GetEnumerator());
queryableMock.Setup(x => x.ElementType).Returns(entities.ElementType);
// mock CreateQuery, without this Linq.Where throwing "System.NotSupportedException: 'Source Provider must be a INhQueryProvider'"
queryableProviderMock.As<INhQueryProvider>()
.Setup(x => x.CreateQuery<RequestRole>(It.IsAny<Expression>()))
.Returns(queryableMock.Object);
var session = new Mock<ISession>();
session.Setup(s => s.Query<RequestRole>()).Returns(queryableMock.Object);
var returns = session.Object.Query<RequestRole>();
// check work
var tolistasync = await returns
.Where(x => x.Id != 0)
.ToListAsync();
Run Code Online (Sandbox Code Playgroud)
在这种情况下Linq.Where,条件不起作用,因为我设置了相同的对象而不是过滤。看来我应该正确地模拟INhQueryProvider.CreateQuery,但是如何呢?
您需要指示 CreateQuery 使用该表达式。正如您所看到的,仅返回模拟的可查询不会执行任何操作。此外,CreateQuery 将需要返回一个 IQueryable 以及实现 INhQueryProvider 的提供程序。问题是 Provider 属性没有设置器,因此您无法在现有的可查询对象上设置它。
我解决类似问题的方法是创建我自己的序列,我可以在其中设置提供程序。
首先创建实现IQueryable<T>和 的类INhQueryProvider;为简洁起见,我仅实现通过 OP 用例所需的内容。请注意,CreateQuery<T>返回一个可查询的提供者,该提供者实现INhQueryProvider:
public class TestingQueryable<T> : IQueryable<T>
{
private readonly IQueryable<T> _queryable;
public TestingQueryable(IQueryable<T> queryable)
{
_queryable = queryable;
Provider = new TestingQueryProvider<T>(_queryable);
}
public Type ElementType => _queryable.ElementType;
public Expression Expression => _queryable.Expression;
public IQueryProvider Provider { get; }
public IEnumerator<T> GetEnumerator()
{
return _queryable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _queryable.GetEnumerator();
}
}
public class TestingQueryProvider<T> : INhQueryProvider
{
public TestingQueryProvider(IQueryable<T> source)
{
Source = source;
}
public IQueryable<T> Source { get; set; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestingQueryable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
return Source.Provider.Execute<TResult>(expression);
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
public int ExecuteDml<T1>(QueryMode queryMode, Expression expression)
{
throw new NotImplementedException();
}
public Task<int> ExecuteDmlAsync<T1>(QueryMode queryMode, Expression expression, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public IFutureEnumerable<TResult> ExecuteFuture<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public IFutureValue<TResult> ExecuteFutureValue<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
throw new NotImplementedException();
}
}
Run Code Online (Sandbox Code Playgroud)
更新您的查询提供程序设置以使用 IQueryable 实现:
queryProviderMock
.Setup(x => x.CreateQuery<RequestRole>(It.IsAny<Expression>()))
.Returns((Expression providedExpression) =>
{
return new TestingQueryable<RequestRole>(queryable.Provider.CreateQuery<RequestRole>(providedExpression));
});
Run Code Online (Sandbox Code Playgroud)
运行.Where(x => x.Id != 0).ToListAsync()并得到预期结果:
您可以更进一步,只需设置 ISession 模拟来使用您的 IQueryable 实现,如果您不需要专门模拟它,则无需模拟查询提供程序。如果您知道我的意思,我通常不会嘲笑模拟返回的内容,这样这将符合我的同行评审标准。
[Test]
public async Task Test2()
{
var requestRoles = new List<RequestRole>();
requestRoles.Add(new RequestRole { Id = 0, RequestOperator = new RequestOperator { Id = 1 } });
requestRoles.Add(new RequestRole { Id = 1, RequestOperator = new RequestOperator { Id = 2 } });
var sessionMock = new Mock<ISession>();
sessionMock.Setup(s => s.Query<RequestRole>()).Returns(new TestingQueryable<RequestRole>(requestRoles.AsQueryable()));
var query = sessionMock.Object.Query<RequestRole>();
var result = await query.Where(x => x.Id != 0).ToListAsync();
Assert.Multiple(() =>
{
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result.Single(), Is.EqualTo(requestRoles.Last()));
});
}
Run Code Online (Sandbox Code Playgroud)