Daily Archives: March 22, 2010

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