C# 如何在使用moq的单元测试中模拟Nhibernate.toListSync()?

C# 如何在使用moq的单元测试中模拟Nhibernate.toListSync()?,c#,unit-testing,nhibernate,moq,C#,Unit Testing,Nhibernate,Moq,我正在尝试使用moq在ASP.NET核心MVC应用程序中创建单元测试。不幸的是,Nhibernate.ToListAsync()不支持LinqIQueryabledataset和throwSystem.NotSupportedException:“源提供程序必须是InQueryProvider”。 在此代码中,我模拟QueryProvider中的,但这还不够: var entities = new List<RequestRole> { new RequestRole()

我正在尝试使用moq在ASP.NET核心MVC应用程序中创建单元测试。不幸的是,
Nhibernate.ToListAsync()
不支持Linq
IQueryable
dataset和throw
System.NotSupportedException:“源提供程序必须是InQueryProvider”
。 在此代码中,我模拟QueryProvider中的
,但这还不够:

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();
var实体=新列表
{
新请求角色()
{
Id=0,
RequestOperator=newrequestoperator(){Id=1}
},
新请求角色()
{
Id=1,
RequestOperator=newrequestoperator(){Id=2}
}
}
.AsQueryable();
//对于ToListAsync,在QueryProvider中同步Mock并将其设置为IQueryable
var queryableProviderMock=new Mock();
queryableProviderMock.Setup(x=>x.ExecuteAsync(It.IsAny(),It.IsAny())
.ReturnsAsync(实体);
var queryableMock=new Mock();
Setup(x=>x.Provider).Returns(queryableProviderMock.Object);
Setup(x=>x.Expression).Returns(entities.Expression);
Setup(x=>x.GetEnumerator()).Returns(entities.GetEnumerator());
Setup(x=>x.ElementType).Returns(entities.ElementType);
//模拟CreateQuery,不带此Linq.Where抛出“System.NotSupportedException:'源提供程序必须是INhQueryProvider'”
queryableProviderMock.As()
.Setup(x=>x.CreateQuery(It.IsAny()))
.Returns(queryableMock.Object);
var session=newmock();
session.Setup(s=>s.Query()).Returns(queryableMock.Object);
var返回=session.Object.Query();
//检查工作
var tolistasync=等待返回
.其中(x=>x.Id!=0)
.ToListAsync();
在本例中,
Linq.Where
条件不起作用,因为我设置了相同的对象而不是过滤对象。
似乎我应该正确地模拟QueryProvider.CreateQuery中的
,但是如何模拟?

您需要指示CreateQuery使用表达式。只是返回模拟的queryable不会像您看到的那样做任何事情。此外,CreateQuery将需要返回一个iQueryTable,其中包含一个实现InQueryProvider的提供程序。问题是Provider属性没有setter,因此无法在现有的queryable上设置它

我解决类似问题的方法是创建自己的序列,在其中可以设置提供者

首先创建实现
IQueryable
inQueryProvider
的类;为了简单起见,我只实现了通过OP用例所需的功能。请注意,
CreateQuery
返回一个查询表,其中提供程序实现了
inqueryProvider

公共类TestingQueryable:IQueryable
{
私有只读可查询;
公共测试可查询(IQueryable可查询)
{
_可查询=可查询;
提供者=新的测试查询提供者(_queryable);
}
公共类型ElementType=>\u queryable.ElementType;
公共表达式=>\u queryable.Expression;
公共IQueryProvider提供程序{get;}
公共IEnumerator GetEnumerator()
{
返回_queryable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
返回_queryable.GetEnumerator();
}
}
公共类测试QueryProvider:InQueryProvider
{
公共测试查询提供程序(iQueryTable源)
{
来源=来源;
}
公共IQueryable源{get;set;}
公共IQueryable CreateQuery(表达式)
{
抛出新的NotImplementedException();
}
公共IQueryable CreateQuery(表达式)
{
返回新的TestingQueryable(Source.Provider.CreateQuery(expression));
}
公共对象执行(表达式)
{
抛出新的NotImplementedException();
}
公共TResult执行(表达式)
{
返回Source.Provider.Execute(表达式);
}
公共任务ExecuteAsync(表达式表达式,CancellationToken CancellationToken)
{
返回Task.FromResult(执行(表达式));
}
public int ExecuteDml(QueryMode QueryMode,表达式)
{
抛出新的NotImplementedException();
}
公共任务ExecuteDmlAsync(QueryMode QueryMode,表达式表达式,CancellationToken CancellationToken)
{
抛出新的NotImplementedException();
}
公共IFutureEnumerable执行未来(表达式)
{
抛出新的NotImplementedException();
}
公共IFutureValue ExecuteFutureValue(表达式)
{
抛出新的NotImplementedException();
}
public void SetResultTransformerAndAdditionalCriteria(IQuery查询、NhLinqExpression、nhExpression、IDictionary参数)
{
抛出新的NotImplementedException();
}
}
更新查询提供程序设置以使用IQueryable实现:

queryProviderMock
.Setup(x=>x.CreateQuery(It.IsAny()))
.Returns((表达式提供的表达式)=>
{           
返回新的TestingQueryable(queryable.Provider.CreateQuery(providedExpression));
});
运行
.Where(x=>x.Id!=0).toListSync()
并获得预期结果:

您可以更进一步,只需设置ISession mock以使用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()));
    });
}
[测试]
公共异步任务Test2()
{
var requestRoles=new List();
Add(newrequestrole{Id=0,RequestOperator=newrequestoperator{Id=1});
Add(newrequestrole{Id=1,RequestOperator=newrequestoperator{Id=2});
var sessionMock=new Mock();
Setup(s=>s.Query())。返回(新的TestingQueryable(requestRoles.AsQueryab