使用Moq模拟nHibernate QueryOver

测试时,以下行以空引用失败:

var awards = _session.QueryOver().Where(x => x.BusinessId == (int)business).List(); 

我的测试是这样的:

 var mockQueryOver = new Mock<IQueryOver>(); mockQueryOver.Setup(q => q.List()).Returns(new List {_awardingBody}); _mockSession.Setup(c => c.QueryOver()).Returns((mockQueryOver.Object)); _mockCommandRunner = new Mock(); _generator = new CertificateGeneratorForOpenSSLCommandLine(_mockSession.Object, _mockCommandRunner.Object, _mockDirectory.Object, _mockFile.Object, _mockConfig.Object); 

老实说,我在黑暗中徘徊 – 我对nHibernate和Moq相对较新,所以我不太确定谷歌要获取正确的信息。

我不认为上面的代码是对的。 AFAIK QueryOver是ISession接口上的一个扩展方法,你不能像那样模拟扩展方法(至少不能使用传统的Mocking工具,比如Moq或RhinoMocks)。

这不是一个好主意。 你不应该嘲笑你不拥有的类型 。 相反,您应该引入一个Repository ,将其接口基于域/业务语言,并使用NHibernate实现它。 实现可以使用ICriteria,HQL,QueryOver,Linq等。重点是这个决​​定将被封装并隐藏在使用存储库的代码中。

您可以编写一个集成测试来测试您的接口+ Real ORM + Real或Fake数据库的组合。 请看一下这个以及关于测试存储库和数据访问的答案。 测试使用Repository的代码也非常简单,因为您可以模拟Repository接口。

除了可测试性之外,这种方法有哪些优点? 它们彼此有些相关:

  • 关注点分离。 数据访问层解决了数据访问问题(存储库实现)。
  • 松耦合。 系统的其余部分没有耦合到当天的数据访问工具。 您有可能将存储库实现从NHibernate切换到原始sql,azure,Web服务。 即使您永远不需要切换,如果您设计’好像’需要切换,分层也会更好地实施。
  • 可读性。 域对象(包括存储库接口定义)基于业务/域语言。

我过去曾使用过几种方法。 正如其他人所建议的那样,一种方法是创建一个Repository类,您可以使用mock / stub封装查询。 这样做的一个问题是,它不是非常灵活,你最终会有一个类似于解决方案的存储过程,除了这个是代码而不是数据库。

我尝试过的最新解决方案是在创建QueryOver存根时创建一个QueryOver存根。 然后,我可以提供应该返回的项目列表。 请记住,如果使用此方法,您不仅应编写unit testing,还应编写集成测试,以测试查询是否真正有效。

 public class QueryOverStub : IQueryOver { private readonly TRoot _singleOrDefault; private readonly IList _list; private readonly ICriteria _root = MockRepository.GenerateStub(); public QueryOverStub(IList list) { _list = list; } public QueryOverStub(TRoot singleOrDefault) { _singleOrDefault = singleOrDefault; } public ICriteria UnderlyingCriteria { get { return _root; } } public ICriteria RootCriteria { get { return _root; } } public IList List() { return _list; } public IList List() { throw new NotImplementedException(); } public IQueryOver ToRowCountQuery() { throw new NotImplementedException(); } public IQueryOver ToRowCountInt64Query() { throw new NotImplementedException(); } public int RowCount() { return _list.Count; } public long RowCountInt64() { throw new NotImplementedException(); } public TRoot SingleOrDefault() { return _singleOrDefault; } public U SingleOrDefault() { throw new NotImplementedException(); } public IEnumerable Future() { return _list; } public IEnumerable Future() { throw new NotImplementedException(); } public IFutureValue FutureValue() { throw new NotImplementedException(); } public IFutureValue FutureValue() { throw new NotImplementedException(); } public IQueryOver Clone() { throw new NotImplementedException(); } public IQueryOver ClearOrders() { return this; } public IQueryOver Skip(int firstResult) { return this; } public IQueryOver Take(int maxResults) { return this; } public IQueryOver Cacheable() { return this; } public IQueryOver CacheMode(CacheMode cacheMode) { return this; } public IQueryOver CacheRegion(string cacheRegion) { return this; } public IQueryOver And(Expression> expression) { return this; } public IQueryOver And(Expression> expression) { return this; } public IQueryOver And(ICriterion expression) { return this; } public IQueryOver AndNot(Expression> expression) { return this; } public IQueryOver AndNot(Expression> expression) { return this; } public IQueryOverRestrictionBuilder AndRestrictionOn(Expression> expression) { throw new NotImplementedException(); } public IQueryOverRestrictionBuilder AndRestrictionOn(Expression> expression) { throw new NotImplementedException(); } public IQueryOver Where(Expression> expression) { return this; } public IQueryOver Where(Expression> expression) { return this; } public IQueryOver Where(ICriterion expression) { return this; } public IQueryOver WhereNot(Expression> expression) { return this; } public IQueryOver WhereNot(Expression> expression) { return this; } public IQueryOverRestrictionBuilder WhereRestrictionOn(Expression> expression) { return new IQueryOverRestrictionBuilder(this, "prop"); } public IQueryOverRestrictionBuilder WhereRestrictionOn(Expression> expression) { return new IQueryOverRestrictionBuilder(this, "prop"); } public IQueryOver Select(params Expression>[] projections) { return this; } public IQueryOver Select(params IProjection[] projections) { return this; } public IQueryOver SelectList(Func, QueryOverProjectionBuilder> list) { return this; } public IQueryOverOrderBuilder OrderBy(Expression> path) { return new IQueryOverOrderBuilder(this, path); } public IQueryOverOrderBuilder OrderBy(Expression> path) { return new IQueryOverOrderBuilder(this, path, false); } public IQueryOverOrderBuilder OrderBy(IProjection projection) { return new IQueryOverOrderBuilder(this, projection); } public IQueryOverOrderBuilder OrderByAlias(Expression> path) { return new IQueryOverOrderBuilder(this, path, true); } public IQueryOverOrderBuilder ThenBy(Expression> path) { return new IQueryOverOrderBuilder(this, path); } public IQueryOverOrderBuilder ThenBy(Expression> path) { return new IQueryOverOrderBuilder(this, path, false); } public IQueryOverOrderBuilder ThenBy(IProjection projection) { return new IQueryOverOrderBuilder(this, projection); } public IQueryOverOrderBuilder ThenByAlias(Expression> path) { return new IQueryOverOrderBuilder(this, path, true); } public IQueryOver TransformUsing(IResultTransformer resultTransformer) { return this; } public IQueryOverFetchBuilder Fetch(Expression> path) { return new IQueryOverFetchBuilder(this, path); } public IQueryOverLockBuilder Lock() { throw new NotImplementedException(); } public IQueryOverLockBuilder Lock(Expression> alias) { throw new NotImplementedException(); } public IQueryOver JoinQueryOver(Expression> path) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression> path) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression> path, Expression> alias) { return new QueryOverStub(_list); } public IQueryOver JoinQueryOver(Expression> path, Expression> alias) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression> path, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression> path, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression> path, Expression> alias, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression> path, Expression> alias, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path, Expression> alias) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path, Expression> alias) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path, Expression> alias, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinQueryOver(Expression>> path, Expression> alias, JoinType joinType) { return new QueryOverStub(new List()); } public IQueryOver JoinAlias(Expression> path, Expression> alias) { return this; } public IQueryOver JoinAlias(Expression> path, Expression> alias) { return this; } public IQueryOver JoinAlias(Expression> path, Expression> alias, JoinType joinType) { return this; } public IQueryOver JoinAlias(Expression> path, Expression> alias, JoinType joinType) { return this; } public IQueryOverSubqueryBuilder WithSubquery { get { return new IQueryOverSubqueryBuilder(this); } } public IQueryOverJoinBuilder Inner { get { return new IQueryOverJoinBuilder(this, JoinType.InnerJoin); } } public IQueryOverJoinBuilder Left { get { return new IQueryOverJoinBuilder(this, JoinType.LeftOuterJoin); } } public IQueryOverJoinBuilder Right { get { return new IQueryOverJoinBuilder(this, JoinType.RightOuterJoin); } } public IQueryOverJoinBuilder Full { get { return new IQueryOverJoinBuilder(this, JoinType.FullJoin); } } } 

不要试图模拟QueryOver。 相反,定义存储库接口(在内部使用QueryOver)并模拟该接口。

最近我一直在将调用.QueryOver()的代码移动到受保护的虚拟方法,并构建我自己的inheritance自XYZ的TestableXYZ并覆盖该方法并返回一个空列表或其他任何内容。 这样我就不需要存储库来进行测试。