views:

1382

answers:

3

Hi, I am using NHibernate and Rhinomocks and having trouble testing what I want. I would like to test the following repository method without hitting the database (where _session is injected into the repository as ISession):

public class Repository : IRepository
{
    (... code snipped for brevity ...)

    public T FindBy<T>(Expression<Func<T, bool>> where)
    {  
        return _session.Linq<T>().Where(where).FirstOrDefault();
    }
}

My initial approach is to mock ISession, and return an IQueryable stub (hand coded) when Linq is called. I have a IList of Customer objects I would like to query in memeory to test my Linq query code without hitting the db. And I'm not sure what this would look like. Do I write my own implementation of IQueryable? If so, has someone done this for this approach? Or do I need to look at other avenues?

Thanks!

A: 

From my point of view is this would be considered Integration Testing. NHibernate has it's own tests that it passes and it seems to me like you're trying duplicate some of those tests in your own test suite. I'd either add the NHibernate code and tests to your project and add this there along with their tests, thats if they don't have one very similiar, and use their methods of testing or move this to an Integration testing scenario and hit the database.

If it's just the fact you don't want to have to setup a database to test against you're in luck since you're using NHibernate. With some googling you can find quite a few examples of how to use SQLite to "kinda" do integration testing with the database but keep it in memory.

Mark G
A: 

@Mark G Thanks for the reply.

We are currently treating the repositories as integration tests, but the tests are a pain point for us. When testing more complex querying, like a search screen for example with multiple search criteria, it takes a ton of work to get the database to the correct state for each test.

We would like to use the PersistenceSpecification.VerifyTheMappings() for our integration tests. If you think about it, that's all that is really needed to test integration, can the system insert and read-back that object from the db? It has nothing to do with the querying code, which should be testable in isolation if we really have loose coupling. Isn't that the whole point of having an ISession instead of the concrete Session?

I'm just not sure how to accomplish it.

Pragmatic Agilist
+5  A: 

How I've done this test is to not pass the expression to the repository, instead expose IQueryable giving the repository an interface such as:

public interface IRepository<T>
{
    IQueryable<T> All();
    // whatever else you want
}

Easily implemented like so:

public IQueryable<T> All()
{
    return session.Linq<T>();
}

This means that instead of calling your method on the repository like:

var result = repository.FindBy(x => x.Id == 1);

You can do:

var result = repository.All().Where(x => x.Id == 1);

Or the LINQ syntax:

var result = from instance in repository.All()
             where instance.Id == 1
             select instance;

This then means you can get the same test by mocking the repository out directly which should be easier. You just get the mock to return a list you have created and called AsQueryable() on.

As you have pointed out, the point of this is to let you test the logic of your queries without involving the database which would slow them down dramatically.

Garry Shutler
How do you mock the code if your code under test also calls result.FirstOrDefault(); ?
David Gardiner
If you create a list and call AsQueryable() on it then .FirstOrDefault() works directly against the contents of the original list. You don't have to mock any calls against the queryable as it is a real, IQueryable object.
Garry Shutler