17

In my project I am using the following approach to querying data from the database:

  1. Use a generic repository that can return any type and is not bound to one type, i.e. IRepository.Get<T> instead of IRepository<T>.Get. NHibernates ISession is an example of such a repository.
  2. Use extension methods on IQueryable<T> with a specific T to encapsulate recurring queries, e.g.

    public static IQueryable<Invoice> ByInvoiceType(this IQueryable<Invoice> q,
                                                    InvoiceType invoiceType)
    {
        return q.Where(x => x.InvoiceType == invoiceType);
    }
    

Usage would be like this:

var result = session.Query<Invoice>().ByInvoiceType(InvoiceType.NormalInvoice);

Now assume I have a public method I want to test that uses this query. I want to test the three possible cases:

  1. The query returns 0 invoices
  2. The query returns 1 invoice
  3. The query returns multiple invoices

My problem now is: What to mock?

  • I can't mock ByInvoiceType because it is an extension method, or can I?
  • I can't even mock Query for the same reason.
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • Not sure what mocking framework you're using (if any), but you could do with Moq. http://blogs.clariusconsulting.net/kzu/how-to-mock-extension-methods/ – Alex Mar 29 '12 at 08:27
  • See this SO thread http://stackoverflow.com/questions/1828878/mocking-an-nhibernate-isession-with-moq – Karel Frajták Mar 29 '12 at 08:28
  • You didnt explain what Query is... If its a method on your interface then surely you can (and should) mock that. In this case, you should definatly not mock ByInvoiceType since that's the type you want to test. You can mock extension methods with Moles btw. – Polity Mar 29 '12 at 08:29
  • @Polity: I *did* explain how my queries are defined. See the method `ByInvoiceType` – Daniel Hilgarth Mar 29 '12 at 08:31
  • @Alex: I am using NSubstitute – Daniel Hilgarth Mar 29 '12 at 08:31
  • @KarelFrajtak: Thanks for the link, but that's not what I want to do. If I would go this road, it would mean to create a database for every test and fill it with demo data. This is SLOW compared to a mocked interface. – Daniel Hilgarth Mar 29 '12 at 08:33
  • Honestly i have a hard time grabbing the problem. From what i understood: you have a Session which is of type: ISession and exposes a method called Query which returns a typed result set. You have an extension method that does some filtering over a typed result set and you have a public method that performs this filter over the typed result set coming from the session? You want to mock the Query method, use a mocking framework, you want to mock the extension method use moles. Where is the problem? – Polity Mar 29 '12 at 08:40
  • @Alex: Thanks for your link. That's a very interesting approach I haven't thought about yet at all. However, I am not quite sure that I can use it in my scenario. But I sure will think about it. – Daniel Hilgarth Mar 29 '12 at 08:41
  • Using in-memory database can be slower than using mocked interfaces. But mocking complex interface, which `ISession` is, can be overkill. – Karel Frajták Mar 29 '12 at 08:43
  • @Polity: I am sorry that my question seems to be unclear. I am not using Moles, because I would hope I can go without it. I can't simply mock the `Query` method because it in itself is an extension method. I can't provide a mock for `ISession`, because that would require me to know the inner workings of the `Query` extension method. – Daniel Hilgarth Mar 29 '12 at 08:44
  • @KarelFrajtak: That's my point. I am sure not going to mock ISession. That's why I am asking. – Daniel Hilgarth Mar 29 '12 at 08:44
  • @Daniel Yup,its quite a cool idea. And its (mock)framework agnostic. – Alex Mar 29 '12 at 09:00
  • @Alex: You are right. But I don't think it will help in my case. The reason is that I don't always use query extension methods. It is perfectly normal to specify the criterion directly: `session.Query().Where(x => x.Barcode = barcode)` – Daniel Hilgarth Mar 29 '12 at 09:57

8 Answers8

31

After some more research and based on the answers here and on these links, I decided to completely re-design my API.

The basic concept is to completely disallow custom queries in the business code. This solves two problems:

  1. The testability is improved
  2. The problems outlined in Mark's blog post can no longer happen. The business layer no longer needs implicit knowledge about the datastore being used to know which operations are allowed on the IQueryable<T> and which are not.

In the business code, a query now looks like this:

IEnumerable<Invoice> inv = repository.Query
                                     .Invoices.ThatAre
                                              .Started()
                                              .Unfinished()
                                              .And.WithoutError();

// or

IEnumerable<Invoice> inv = repository.Query.Invoices.ThatAre.Started();

// or

Invoice inv = repository.Query.Invoices.ByInvoiceNumber(invoiceNumber);

In practice this is implemented like this:

As Vytautas Mackonis suggested in his answer, I am no longer depending directly on NHibernate's ISession, instead I am now depending on an IRepository.

This interface has a property named Query of type IQueries. For each entity the business layer needs to query there is a property in IQueries. Each property has its own interface that defines the queries for the entity. Each query interface implements the generic IQuery<T> interface which in turn implementes IEnumerable<T>, leading to the very clean DSL like syntax seen above.

Some code:

public interface IRepository
{
    IQueries Queries { get; }
}

public interface IQueries
{
    IInvoiceQuery Invoices { get; }
    IUserQuery Users { get; }
}

public interface IQuery<T> : IEnumerable<T>
{
    T Single();
    T SingleOrDefault();
    T First();
    T FirstOrDefault();
}

public interface IInvoiceQuery : IQuery<Invoice>
{
    IInvoiceQuery Started();
    IInvoiceQuery Unfinished();
    IInvoiceQuery WithoutError();
    Invoice ByInvoiceNumber(string invoiceNumber);
}

This fluent querying syntax allows the business layer to combine the supplied queries to take full advantage of the underlying ORM's capabilities to let the database filter as much as possible.

The implementation for NHibernate would look something like this:

public class NHibernateInvoiceQuery : IInvoiceQuery
{
    IQueryable<Invoice> _query;

    public NHibernateInvoiceQuery(ISession session)
    {
        _query = session.Query<Invoice>();
    }

    public IInvoiceQuery Started()
    {
        _query = _query.Where(x => x.IsStarted);
        return this;
    }

    public IInvoiceQuery WithoutError()
    {
        _query = _query.Where(x => !x.HasError);
        return this;
    }

    public Invoice ByInvoiceNumber(string invoiceNumber)
    {
        return _query.SingleOrDefault(x => x.InvoiceNumber == invoiceNumber);
    }

    public IEnumerator<Invoice> GetEnumerator()
    {
        return _query.GetEnumerator();
    }

    // ...
} 

In my real implementation I extracted most of the infrastructure code into a base class, so that it becomes very easy to create a new query object for a new entity. Adding a new query to an existing entity is also very simple.

The nice thing about this is that the business layer is completely free of querying logic and thus the data store can be switched easily. Or one could implement one of the queries using the criteria API or get the data from another data source. The business layer would be oblivious to these details.

Community
  • 1
  • 1
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • 1
    do you have sample app with this API? I would really like to see this in a small simple but complete app? Do you have something like that? Thanks! – zam6ak Apr 03 '12 at 15:55
  • @zam6ak: Currently not. I might be able to provide something next week. – Daniel Hilgarth Apr 04 '12 at 09:04
  • that would be great! Please post the update here once you have something. Thank you in advance! – zam6ak Apr 04 '12 at 18:26
  • +1 Very elegant! Exactly what I've been searching for. Nicely done. – Dave T. Mar 15 '13 at 21:32
  • Very elegant for sure, solves the issues of allowing query building to correctly belong to the business layer while allowing data access concerns to belong apart from the business layer. Bravo – tuespetre Sep 28 '13 at 19:25
  • One question though: how do you handle paging and sorting? – tuespetre Sep 28 '13 at 20:48
  • You could easily add `Skip` and `Take` to `IQuery`. If the implementation of this interface was implemented immutable, you could save the query in a field and request a specific page with Skip and Take. – Daniel Hilgarth Sep 30 '13 at 02:39
  • 1
    Just got here from Mark Seemmann blog). One advice: filtration can be more general, for example, StartIs(false), FinishIs(bool), etc.. – Artur A Dec 10 '13 at 22:00
  • I really like this approach. However, I would advise anyone looking to implement it that the query builder should be immutable. The methods should return a new instance, rather than modify the existing one. This allows you to build up and reuse parts of the query builder. – Alex Jun 05 '14 at 14:48
  • 1
    @AlexG: That's a good point that also has been raised on my blog. Please see [this post](http://blog.fire-development.com/2013/06/25/a-fluent-repository-immutability/) for an immutable version. – Daniel Hilgarth Jun 06 '14 at 09:10
  • @DanielHilgarth Ah, you got there before me :) I'm also interested in how you unit test with the query builder - do you just mock it out and check that it received a particular call? – Alex Jun 06 '14 at 09:50
  • Don't fear the IQueryable: http://blogs.msdn.com/b/tilovell/archive/2014/09/15/is-iqueryable-poisoning-your-interfaces.aspx – Tim Lovell-Smith Sep 15 '14 at 16:46
  • How has this worked out for you since 2014? Looking at possibly using a similar approach but what if your business logic domain model differs from your data storage model? How are you handling that? – ryanulit Apr 28 '17 at 18:20
  • @ryanulit I have stopped doing this and instead, I am now using very successfully the approach outlined [here](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91) and [here](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92) – Daniel Hilgarth Apr 28 '17 at 18:39
  • @DanielHilgarth Funny you mention that, as that exact setup is what I am currently using. I've been debating trying this method out b/c if I change data stores, I'd have to rewrite every separate query class vs one class of queries, though now that I talk this through it's pretty much the same amount of work in the end and the CQS approach is a little cleaner. – ryanulit Apr 28 '17 at 19:00
2

ISession would be the thing you should mock in this case. But the real problem is that you should not have it as a direct dependency. It kills testability the same way as having SqlConnection in the class - you would then have to "mock" the database itself.

Wrap ISession with some interface and it all becomes easy:

public interface IDataStore
{
    IQueryable<T> Query<T>();
}

public class NHibernateDataStore : IDataStore
{
    private readonly ISession _session;

    public NHibernateDataStore(ISession session)
    {
        _session = session;
    }

    public IQueryable<T> Query<T>()
    {
        return _session.Query<T>();
    }
}

Then you could mock IDataStore by returning a simple list.

  • but if you would want to test NHibernateDataStore, then you would have to mock ISession.Query and that is not possible because Query is an extension method – evictednoise Sep 12 '17 at 13:58
  • NHibernateDataStore itself is pointless to test by mocking anyway since it only delegates and you do not control ISession interface. Should avoid mocking types you do not own. – Vytautas Mackonis Sep 12 '17 at 18:47
  • How would you suggest approaching coding NHibernateDataStore in a TDD situation? I can't think of any other tests than ones checking if delegation is actually done. – evictednoise Sep 12 '17 at 20:05
  • You should integration test this (using a real database and whatnot). However, IQueryable is next to impossible to integration test (see http://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/), hence if you REALLY want to cover this with tests, you should narrow down the interface - for example, use multiple methods with parameters or specification pattern instead. Testing delegation with mocking will only give a false sense of security since code may break in numerous ways when LINQ provider cannot translate the actual query while your tests still pass. – Vytautas Mackonis Sep 12 '17 at 23:54
0

I know that this has long been answered and I do like the accepted answer, but for anybody running into a similar problem I would recommend looking into implementing the specification pattern as described here.

We've been doing this in our current project for more than a year now and everyone likes it. In most cases your repositories only need one method like

IEnumerable<MyEntity> GetBySpecification(ISpecification<MyEntity> spec)

And that's very easy to mock.

Edit:

The key to using the pattern with an OR-Mapper like NHibernate is having your specifications expose an expression tree, which the ORM's Linq provider can parse. Please follow the link to the article I mentioned above for further details.

public interface ISpecification<T>
{
   Expression<Func<T, bool>> SpecExpression { get; }
   bool IsSatisfiedBy(T obj);
}
EagleBeak
  • 6,939
  • 8
  • 31
  • 47
  • I always wonder when people suggest the specification pattern: How does the implementation look like? And are you really implementing the whole pattern, i.e. are you supporting composite specifications and the default operations And, Or and Not? Don't get me wrong: I like that pattern. But I never really saw how I would use it against a database, where I can't simply pull every object and pass it to the specification. – Daniel Hilgarth Jul 24 '13 at 10:43
  • Please see my last edit. It's really not that difficult. The crucial bottleneck IMO is the ORM's Linq provider, which might not support a certain expression you provide. – EagleBeak Jul 24 '13 at 12:03
  • OK, so you implement it by using Expressions. Yes, that would certainly work, but it would tightly couple the specifications to the ORM in use, although that is not evident at first glance. Still, it is a good possibility. – Daniel Hilgarth Jul 24 '13 at 12:05
  • Actually I don't find the coupling that tight. The expressions are in no way directly dependent on the ORM. It does force you to use some kind of lowest common denominator of expressions supported by all ORM's you might intend to use at some point. And I agree that this is a design smell (a persistence concern leaking into the domain). But in practice that hasn't really been a problem. And I doubt that this would pop up as a problem when switching ORM implementations (which hardly ever happens in most projects anyway). – EagleBeak Jul 24 '13 at 12:29
  • Agreed, that switching ORMs doesn't happen very often. But the design smell still is something I wouldn't want to have. And it's actually not even the problem with the lowest common denominator. Even if you just want to support one ORM, you still need to know which ORM it is and - much more importantly - you need to know which operations are supported and which are not. To address this design smell, one might think about moving the implementation of the specifications to the DAL. However, this raises the question on how the domain gets a hold of the specifications it wants to use. – Daniel Hilgarth Jul 24 '13 at 12:35
  • I have yet to see a solution for this smell. We have toyed around with the idea of moving spec implementations to the DAL too, but abandoned it, because it seemed too much overhead when you create new specs. One of the main advantages of this pattern is, that it's so easy to extend and compose existing specs. We didn't want to sacrifice that. If you have any ideas about how to achieve that with minimum overhead, I'd be more than excited to hear that. – EagleBeak Jul 24 '13 at 12:45
  • Unfortunately, I have none without overhead. Still looking for them myself... Anyway, thanks for your answer and the discussion! – Daniel Hilgarth Jul 24 '13 at 12:50
0

The answer is (IMO): you should mock Query().

The caveat is: I say this in total ignorance of how Query is defined here - I don't even known NHibernate, and whether it is defined as virtual.

But it probably doesn't matter!Basically what I would do is:

-Mock Query to return a mock IQueryable. (If you can't mock Query because it's not virtual, then create your own interface ISession, which exposes a mockable query, and so on.) -The mock IQueryable doesn't actually analyze the query it is passed, it just returns some predetermined results that you specify when you create the mock.

All put together this basically lets you mock out your extension method whenever you want to.

For more about the general idea of doing extension method queries and a simple mock IQueryable implementation, see here:

http://blogs.msdn.com/b/tilovell/archive/2014/09/12/how-to-make-your-ef-queries-testable.aspx

Tim Lovell-Smith
  • 15,310
  • 14
  • 76
  • 93
0

To isolate testing just to just the extension method i wouldn't mock anything. Create a list of Invoices in a List() with predefined values for each of the 3 tests and then invoke the extension method on the fakeInvoiceList.AsQueryable() and test the results.

Create entities in memory in a fakeList.

var testList = new List<Invoice>();
testList.Add(new Invoice {...});

var result = testList().AsQueryable().ByInvoiceType(enumValue).ToList();

// test results
Quinton Bernhardt
  • 4,773
  • 19
  • 28
  • Sorry, I don't follow. The code I want to use uses the NHibernate `ISession` and I can't change that fact in my test. So where and how would I use that `fakeInvoiceList`? **BTW: I don't want to test the extension method. I want to test the method that processes the result of this extension method.** – Daniel Hilgarth Mar 29 '12 at 08:34
0

If it suits your conditions, you can hijack generics to overload extension methods. Lets take the following example:

interface ISession
{
    // session members
}

class FakeSession : ISession
{
    public void Query()
    {
        Console.WriteLine("fake implementation");
    }
}

static class ISessionExtensions
{
    public static void Query(this ISession test)
    {
        Console.WriteLine("real implementation");
    }
}

static void Stub1(ISession test)
{
    test.Query(); // calls the real method
}

static void Stub2<TTest>(TTest test) where TTest : FakeSession
{
    test.Query(); // calls the fake method
}
Polity
  • 14,734
  • 2
  • 40
  • 40
  • I thought about something like that, too. But it won't work, because the code I want to test depends on `ISession` and thus will always call the extension method. – Daniel Hilgarth Mar 29 '12 at 09:09
  • The intent is that you change the code you want to test not to depend on ISession but on a generic constrained with the ISession interface. That means that you either have to change existing code or make an ISession proxy. One that simply takes a generic constrained with ISession and internally calls those methods. I can provide you with a sample if you want. – Polity Mar 29 '12 at 09:12
  • I see. A sample is not necessary. However, this would mean to introduce the generic type parameter for the sole purpose of testing. I don't like to do this. I want my classes to be testable, but I don't want to make my classes more complex than they need to be just because of testability. In practice, the class would always be instantiated with `ISession` as the generic type in the production code. – Daniel Hilgarth Mar 29 '12 at 09:17
0

depending on your implementation of Repository.Get, you could mock the NHibernate ISession.

Dirk Trilsbeek
  • 5,873
  • 2
  • 25
  • 23
  • You don't want to mock something like `ISession`. It's too complex. – Daniel Hilgarth Mar 29 '12 at 09:06
  • not necessarily. You don't have to create a stub implementing all methods, you only need a mock to provide a single test with the needed data. Of course you'd also have to mock the part that injects the ISession into the repository. For instance if you create a test for your scenario, you can create an isession mock that returns a list of memory created objects as an IQueryable each time it is called. That will of course only work if you test isolated parts of your application. To mock a complete ISession for an integration test would be quite complicated. – Dirk Trilsbeek Mar 29 '12 at 12:05
  • The problem is that I first would have to dig deep into the code behind the `Query` extension method to even know what I would need to mock. – Daniel Hilgarth Mar 29 '12 at 12:07
  • I'm not entirely sure (my repositories still use the criteria api) but wouldn't you just have to return a List.AsQueryable()? – Dirk Trilsbeek Mar 29 '12 at 12:50
  • ah, sorry. I must have overlooked the fact that ISession.Query is an extension method itself. An in memory database with an actual NHibernate-Session would probably work. – Dirk Trilsbeek Mar 29 '12 at 13:37
0

I see your IRepository as a "UnitOfWork" and your IQueries as a "Repository" (Maybe a fluent repository!). So, simply follow the UnitOfWork and Repository pattern. This is a good practice for EF but you can easily implement your own.

Amir Karimi
  • 5,401
  • 4
  • 32
  • 51