如何使用Moq模拟NHibernate扩展方法?

4

我正在开发一个应用程序,使用NHibernate作为ORM,NUnit进行单元测试,使用Ninject作为我的DI。我正在模拟ISession:

var session = new Mock<ISession>();

对于常规的非模拟会话对象,我可以使用LINQ扩展方法进行查询,示例如下:

var result = Session.Query<MyEntity>();

但是当我尝试使用以下代码进行模拟时...
session.Setup(s => s.Query<MyEntity>());

当我运行时,会出现一个“不支持”的异常:

Expression references a method that does not belong to the mocked object: s => s.Query<MyEntity>()

我该如何在Moq/NHibernate中模拟这样的基本查询?

只是为了理解:https://dev59.com/Rmw15IYBdhLWcg3wFH3f - VikciaR
4
小贴士:不要嘲笑ISession。这些测试会给你带来什么好处?我认为:编写针对NH查询的集成测试。对于单元测试,请使用DAO/存储库等自己的类型/接口隐藏ORM/NH(并且模拟/替代/伪造这些类型)。 - Roger
3
它给你的好处是,你不需要担心实际数据库的状态。你正在进行单元测试,这意味着你应该只测试该方法内部的代码。你的测试不应该关心是否有人删除了数据库。那不是该方法的问题。 - krillgar
2个回答

3

Query<T>()是一个扩展方法,因此您无法模拟它。 尽管@Roger的答案是正确的,但有时具体的测试也很有用。您可以通过阅读NHibernate代码或使用自己的测试来开始调查Query<T>()方法所做的事情,并在ISession上设置适当的方法。

警告:创建这样的设置将使您的测试非常脆弱,并且如果NHibernate的内部实现发生更改,则会中断。

无论如何,您可以从以下内容开始调查:

var mockSession = new Mock<ISession>(MockBehavior.Strict); //this will make the mock to throw on each invocation which is not setup
var entities = mockSession.Object.Query<MyEntity>();

上面的第二行代码将会抛出一个异常并告诉你 Query<T>() 扩展方法试图访问哪个实际的 ISession 属性/方法,所以你可以相应地设置它。继续这样做,最终你将拥有一个良好的会话设置,以便在测试中使用它。
注意:我不熟悉 NHibernate,但当我需要处理其他库的扩展方法时,我使用了上述方法。

这是在构建测试适应性时发现依赖关系的最佳方法。+1 - Raffaeu
这并没有回答关于如何绕过它是一个扩展方法的事实,因此无法进行模拟的问题。 - krillgar
@krillgar,除非我漏掉了什么显而易见的东西,否则第一句话就已经说明了这一点——你不能使用moq(Q标记为moq)来模拟它。 - Sunny Milenov

3

版本5的更新:

在新的NHibernate版本中,Query<T>是ISession接口的一部分,而不是扩展函数,因此很容易进行模拟。

旧的回答:

我尝试了Sunny的建议,但只做到这一步就卡住了,因为IQuery被转换为一个内部的NHibernate.Impl.ExpressionQueryImpl,我认为它无法被扩展。只是为了节省其他迷失的灵魂几个小时而发布此帖。

var sessionImplMock = new Mock<NHibernate.Engine.ISessionImplementor>(MockBehavior.Strict);
var factoryMock = new Mock<NHibernate.Engine.ISessionFactoryImplementor>(MockBehavior.Strict);
var queryMock = new Mock<IQuery>(MockBehavior.Strict);//ExpressionQueryImpl

sessionImplMock.Setup(x => x.Factory).Returns(factoryMock.Object);
sessionImplMock.Setup(x => x.CreateQuery(It.IsAny<IQueryExpression>())).Returns(queryMock.Object);
sessionMock.Setup(x => x.GetSessionImplementation()).Returns(sessionImplMock.Object);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接