Mocking out nHibernate QueryOver with Moq



在测试时,下面一行由于空引用而失败:

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

我的测试是这样的:

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

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

这不是一个好主意。你不应该嘲笑你不属于的类型。相反,你应该引入一个Repository,将其接口基于领域/业务语言,并使用NHibernate实现它。实现可以使用iccriteria, HQL, QueryOver, Linq等。关键是这个决策将被封装,并且对使用存储库的代码隐藏起来。

你可以写一个集成测试,它将测试你的接口+真实ORM +真实或虚假数据库的组合。请看看这个,它回答了关于测试存储库和数据访问的问题。测试使用Repository的代码也非常容易,因为您可以模拟Repository接口。

除了可测试性之外,这种方法的优点是什么?它们之间有些联系:

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

我过去使用过几种方法。正如其他人建议的那样,一种方法是创建一个Repository类,您可以模拟/存根来封装您的查询。这样做的一个问题是,它不是很灵活,您最终会有一个类似于解决方案的存储过程,只不过这个存储过程是在代码中而不是在数据库中。

我最近尝试的一个解决方案是创建一个QueryOver存根,当我存根QueryOver方法时,我提供。然后我可以提供一个应该返回的项目列表。请记住,如果您使用这种方法,您不仅应该编写单元测试,还应该编写集成测试,它将测试查询是否实际工作。

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

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

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

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

NHibernate QueryOver异步代码可以使用c# moq来模拟

var session = new Mock<NHibernate.ISession>();
session.Setup(x => x.QueryOver<Etype>().ListAsync(CancellationToken.None)).ReturnsAsync(data);
_dbContext.Setup(m => m.Session).Returns(session.Object);

最新更新