Mocking out nHibernate QueryOver with Moq
Asked Answered
C

6

11

The following line fails with a null reference, when testing:

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

My test is like so:

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); 

To be honest I'm flailing around in the dark here - I'm relatively new to nHibernate and Moq, so I'm not very sure what to google to get the right information.

Camembert answered 22/9, 2011 at 11:0 Comment(0)
C
2

I don't think the above code is right. AFAIK QueryOver is an extension method on ISession interface and you can not Mock extensions method like that (at least not with conventional Mocking tools like Moq or RhinoMocks).

Charleen answered 22/9, 2011 at 11:25 Comment(1)
QueryOver is not an extension method. You're thinking of QueryToper
S
4

This is not a good idea. You should not mock the types you don't own. Instead you should introduce a Repository, base its interface on domain/business language and implement it using NHibernate. Implementation can use ICriteria, HQL, QueryOver, Linq etc. The point is that this decision will be encapsulated and hidden from the code that consumes repository.

You can write an integration test that will test combination of your interface + Real ORM + Real or Fake database. Please take a look at this and this answers about testing repositories and data access. Testing the code that uses Repository is also super easy because you can mock Repository interface.

What are the advantages of this approach other than testability? They are somewhat related to each other:

  • Separation of Concerns. Data access issues solved in the data access layer (repository implementation).
  • Loose Coupling. The rest of the system is not coupled to the data-access-tool-of-the-day. You have potential for switching repository implementation from NHibernate to raw sql, azure, web service. Even if you never need to switch, the layering is enforced better if you design 'as if' you need to switch.
  • Readability. Domain objects, including repository interface definition are based on business/domain language.
Scaly answered 22/9, 2011 at 16:14 Comment(3)
For any mature application the data access strategy is not decided on a whim and certainly not changed every day. Hiding NHibernate (or EF for that matter) behind a repository means you loose the benefits NHibernate provides (explicit lazy/eager fetching, etc...) unless you declare every possible session.QueryOver/ICriteria permutation in the repository interface. See The false myth of encapsulating data access in the DAL - Ayende @ RahienRobin
@Robin you still have nhibernate under the hood with all its benefits. I prefer to treat nhibernate as an implementation detail and focus on the business logic and making sure my domain model uses that same language as business requirements. You don't need to declare every permutation - you create a meaningful methods like 'orders.Delinquent()' etc. You can also use Specification pattern in rare cases where you can't predict which combination of attributes will be queried on (something like 'advanced search' feature).Scaly
@Robin Regarding Ayende article. I agree that assuming that you can easily switch ORM is unrealistic. But you can still design as_if you want to be able to, and it has huge benefits on the overall quality of the design (readability, testability, loose coupling). Yes switching ORM is hard and most of the time unrealistic but that is not an excuse to create an tightly coupled and untestable ball of mud.Scaly
P
3

Don't try to mock QueryOver. Instead, define a repository interface (which uses QueryOver internally) and mock that interface.

Persis answered 22/9, 2011 at 13:7 Comment(0)
T
3

I've used several approaches in the past. One way is, as others have suggested, to create a Repository class that you can mock/stub that encapsulates your queries. One problem with this is that it's not very flexible and you wind up having a stored procedure like solution, except this one is in code rather than the database.

A recent solution I have tried is creating a QueryOver stub that I provide when I stub the QueryOver method. I can then provide a list of items that should be returned. Keep in mind if you using this approach you should not only write unit tests, but an integration test, which will test whether or not the query actually works.

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); }
    }
}
Toper answered 22/9, 2011 at 19:41 Comment(9)
Mockable and testable Repository that encapsulates data access is like stored procedure? You'd rather recommend spreading QueryOver all over the place? That would destroy 'domain layer' and will spread its responsibilities among UI and data access layer. That would sure be super flexible: en.wikipedia.org/wiki/Big_ball_of_mudScaly
@Dimitry, several points. Yes a repository where you keep all of your queries is exactly like having a set of stored procs. What happens if he needs to query the same entity, but using 2 parameters? Or maybe he just wants a projection? How would using queries as needed 'destroy the domain layer'? Your domain entities still contain the business logic. Besides NHibernate is already an abstraction of data access. Why would you feel like you need to abstract it further? It makes testing easier, but refactoring harder, imo.Toper
@Dimitry also check out these articles by Oren ayende.com/blog/4784/… ayende.com/blog/4783/…Toper
Stored proc. are bad because they are 1) written in archaic procedural language 2) externalize business logic 3)very hard to test 4) reduce scalability. None of these applies to a properly designed Repository. Regarding your 'What happens if?' - what happens is you change the code based on business needs and write tests. Is your approach somehow immune to this? Properly abstracted data access promotes: Separation of Concerns, Loose Coupling, Testability, Readability and more Domain/Business oriented code. If you don't like abstracting it why not use SQL directly? It would be so flexible.Scaly
Regarding articles by Oren. I think that he approaches it from purely mechanical 'data access' perspective. Modern applications are so much more than 'how do we get the data'. By the time you spread latest-and-greatest data access types all over your code base the code is no longer domain-oriented and expressive and is just bunch of tightly coupled, procedural, unchangeable, untestable hairballs. Which is not a problem if were a consultant and its somebody else's job to support it.Scaly
In addition to Oren's blogs it might worth looking at: amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/…, amazon.com/Patterns-Enterprise-Application-Architecture-Martin/…, amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/…, amazon.com/Dependency-Injection-NET-Mark-Seemann/dp/1935182501/…Scaly
The approach of using NHibernate directly is immune from having to create a bunch of custom repository methods that only get used in 1 spot. You can still test against this with the approach I've outlined in the answer (custom stub for unit tests, full db access for integration tests) I still don't understand how not using a repository for data querying mean we're not practicing DDD. Nothing is stopping you from creating rich domain entities. Can you not practice DDD in RoR just because it is common to use ActiveRecord directly to query data?Toper
DDD is a set of guidelines and not a strict formula. You can probably claim that it can be implemented using x86 Assembler and I really don't want to argue with you. Sane DDD implementations that deal with long-lived objects use Repository pattern. The whole point of this pattern is to encapsulate data access technicalities behind domain-driven interface. Not data-driven. When I see QueryOver I see data access. When I see DelinquentOrderSpec I see business requirements.Scaly
ActiveRecord is orthogonal to Domain Model, you either use AR or DM. You can of course implement Domain Model on top of ActiveRecord but what is the point? In my opinion, approach that you promote is equivalent to spreading SQL directly all over the code base. The only advantage is that it offers primitive type safety. In my opinion it promotes tight coupling, discourages unit testing and has no separation of concerns. It is DDD if DDD stand for Data-Driven-Development.Scaly
C
2

I don't think the above code is right. AFAIK QueryOver is an extension method on ISession interface and you can not Mock extensions method like that (at least not with conventional Mocking tools like Moq or RhinoMocks).

Charleen answered 22/9, 2011 at 11:25 Comment(1)
QueryOver is not an extension method. You're thinking of QueryToper
E
1

Lately I've been moving the code that calls .QueryOver() to a protected virtual method instead and building my own TestableXYZ that inherits from XYZ and overrides the method and returns a empty list or whatever. This way I dont need a repository just for testing.

Edwardoedwards answered 30/7, 2012 at 14:11 Comment(0)
E
0

NHibernate QueryOver async code can be mocked using 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);
Envoi answered 9/5, 2022 at 12:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.