Include Method Extension for IObjectSet – What about the mocks?

Eager loading with Entity Framework depends on the special ObjectQuery.Include method. We’ve had that from Day 1 (first version of ef).

Now we use ObjectSets in EF4 which inherit from ObjectQuery (thereby inheriting Include) and also implement IObjectSet.

IObjectSet allows us to break the queries apart from ObjectQuery and ObjectContext so we can write persistent ignorant, testable code.

But IObjectSet doesn’t come with the Include method and you have to create an extension method to continue using it.

This is not new. Lots of people have blogged about it before including myself. Here’s a post I wrote about using Include with IObjectSets: http://thedatafarm.wpengine.com/blog/data-access/agile-entity-framework-4-repository-part-5-iobjectset/

This extension method works great when the IObjectSet is the one for your true EF query, therefore an ObjectSet which inherits from ObjectQuery and can leverage the Include.

But what happens when you are testing a method that contains an eager loaded query with Include and you are using a mock context, not the real EF ObjectContext?

The Include method will get ignored.

For example, you may have a method such as:

 

public List<Customer> GetCustomerWithOrders(int customerId)
{
   return context.Customer.Include(“Orders”)
.Where(c=>c.Id==customerId).ToList(); }

In *real* EF, “context.Customer” is an ObjectSet, but if you are using a mock context, then it’s an IObjectSet with no Include.

All you’ll get back after running through the extension method is the Customer — with no orders. But at least the method won’t crash and burn.

But what if the method does something like this:

public string BuildEmailForCustomerWithOrders(int customerId)
{
   var cust=  context.Customer.Include(“Orders”).FirstOrDefault(c=>c.Id==customerId);
   if (cust.Orders>0)
    return “Dear” + cust.Name + “, Thanks for your order”
   else
    return “Dear” +cust.Name+ “, Screw you for not placing any orders”;
}

You might then have a test to be sure that good customers get a nice email and bad customers get the nasty email.

With the mocks, you can’t test that because Orders will always be 0.

Here’s an easy solution….just create a special mock context that is used anywhere you need to test methods that contain Includes.

And instead of it’s Customers property returning customers, the Customers property should construct a customer with some orders in its Orders property and return that.

Long story to get to a very simple solution – which may be obvious to pro testers – but is not necessarily to us newbies.

I already have a bunch of mocks…good data mocks to test valid validations…bad data mocks to test invalid validations, etc. So creating a mock so that my methods that happen to contain Include queries can still be tested was a no-brainer (once I was confident there was no way (and really, no need) to generically emulate the Include behavior).

  Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!  

4 thoughts on “Include Method Extension for IObjectSet – What about the mocks?

  1. Hi Julie,

    Is there a way to get CreateSourceQuery() to work when you’re mocking (or using an in memory version of your data context – ie IObjectSet)

    The following crashes and burns

    IRepository db = new InMemoryRepository();

    O = db.Orders.First(); O.OrderItems.CreateSourceQuery().ToList();

    It gets O just fine, but when you try to run CreateObjectQuery() I get an exception "Value cannot be null"

    (obviously this example is silly, but I might have a much more complicated query where I actually did want CreatSourceQuery())

    If this is the limit to what can be abstracted and unit tested in EF4, then I can live with that – just want to make sure I’m not missing something.

    Looking forward to your book!

  2. Bah – sorry, CreateSourceQuery returns null when run from the memory context – so it was the ToList() that blew it up – still though, any idea on how to get CreateSourceQuery() to simply return the underlying collection when working with a mocked context, rather than null? Unfortunately EntityCollection<T> is sealed, so no chance of mocking that!

  3. Hacks after hacks. I was hoping that EF 4.0 will make this all great given the feedback it got for the previous release. But it somehow does not feel natural.

    Today, I have a need for Include but some other day I need another method and I need to write another extension method. This goes on.

    – Harsha

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.