Unit Testing on Top of Entity Framework DbContext

When writing unit tests one of the challenges is to isolate your tests from everything. To isolate them from the code that is not in their target and also from the other tests. As Roy Osherove puts it in his book “The Art of Unit Testing

[...] a unit test should always run in its little world, isolated from even the knowledge that other tests there may do similar or different things

Test isolation makes a key difference between tests suites which are maintainable and bring high value and the ones which are a burden and bring little or no value.

In data intensive applications one of the most common difficulties when writing unit tests is to isolate them from the database. We would like that the tests which verify the business logic, not to hit the database, so we can easily configure test data for different test cases and we can easily assert the result.

One of the best approaches to achieve this, is to implement the Repository Pattern with a good abstraction that gives all the necessary functions to do data access. In my previous posts I present some examples. Having this, in our tests we can use stubs or mocks as implementations of the IRepository interface, and we can test the caller code in isolation. The code snippet below shows such a test which verifies that the where clause filters out data. Similarly, tests which verify that the result is ordered by a certain criteria, or that some business calculations are done correctly when the data is read or saved can easily be done.

 [Test]  
 public void GetOrdersForShipment_AlsoPendingOrders_PendingOrdersFilteredOut()  
 {  
     Order orderInPending = new Order {Status = OrderStatus.Pending};  
     Order orderToShip = new Order {Status = OrderStatus.Processed};  
     IRepository repStub = GetRepStubWith(orderInPending, orderToShip);

     var target = new OrderingService(repStub);

     IQueryable<Order> orders = target.GetOrdersForShipment();

     var expected = new[] {orderInPending};  
     AssertEx.AreEquivalent(orders, expected);  
}

private IRepository GetRepStubWith(params Order[] orders)  
{  
     Mock<IRepository> repStub = new Mock<IRepository>();  
     repStub.Setup(r => r.GetEntities<Order>())  
             .Returns(orders.AsQueryable());

     return repStub.Object;  
}

Entity Framework supports testable code designs and the DbContext in itself is a repository implementation. So what would it mean to stub or mock the DbContext directly and write isolated tests in a similar way as we did in the example above? We might need to do this when we don’t wrap the DbContext into another repository implementation or we want to test the code that does the wrapping (as we did in the DataAccess implementation here).

To get this going the first thing we need, is to make sure that the code we want to test does not use directly our specific context class, but its base class which is DbContext. A factory which will be used by the target code instead of newing up a MyDatabaseContext instance, gets this done. In the test code we will have the factory return a stub or a mock for the context.

Let’s start with a simple test: verify that filtering data at read works. So this simple test would look like this:

 // target (production) code  
 class UsersService  
 {  
     private DbContext dbContext;

     public User GetUserById(int id)  
     {  
         return dbContext.Set<User>().FirstOrDefault(x => x.Id == id);  
     }  
     …  
 }

// unit test code, test method  
     …    
    var contextFactory = GetFactoryWithTestData();  
     var target = new UsersService(contextFactory);

     User actual = target.GetUserById(2)

     User expected = new User {Id = 2};  
     Assert.AreEqual(expected, actual);

The test data is not visible here. This is because setting it up requires quite some code, and I’ve put it into the GetFactoryWithTestData() function. First, this function needs to build a stub for DbSet<User>, which contains a few user DTOs among which one has the Id == 2. Second, it has to build and configure a DbContext stub which returns the DbSet stub. In a simplified version the code looks like below:

 …  
 private static DbContext CreateContextWithTestData()  
 {  
     List<User> users = // newup a list with some users DTOs  
       DbSet<User> userSet = GetDbSetStub(users);

     Mock<DbContext> contextStub = new Mock<DbContext>();  
     contextStub.Setup(x => x.Set<User>())  
     .Returns(() => userSet);

     return contextStub.Object;  
 }

…  
 private static DbSet<T> GetDbSetStub<T>(List<T> values) where T : class  
 {  
     return new FakeSet<T>(values);  
 }  
 …  
 class FakeSet<T> : DbSet<T>, IQueryable where T : class  
 {  
     List<T> values;  
     public FakeSet(IEnumerable<T> values)  
     {  
         this.values = values;  
    }

     IQueryProvider IQueryable.Provider  
     {  
         get { return values.AsQueryable().Provider; }  
     }

     Expression IQueryable.Expression  
     {  
         get { return values.AsQueryable().Expression; }  
     }

     Type IQueryable.ElementType  
     {  
         get { return values.AsQueryable().ElementType; }  
     }

     public IList<T> Values  
     {  
         get { return values; }  
     }

     public override T Add(T entity)  
     {  
         values.Add(entity);  
         return entity;  
     }

     public override T Remove(T entity)  
     {  
         values.Remove(entity);  
         return entity;  
     }  
 }  

This works well for testing simple queries. For more complex scenarios, setting up data for one-to-many or many-to-many relations gets quite complex. You could set it once with a model of Users and Roles and use it for more tests, but it is hard to do the same for testing other areas of the application and all the business logic.

Another thing to notice in the above snippet is that we have written the FakeStub class instead of using Moq. This is because we want to keep some state on it (the values) and use it in test cases that involve adding or removing entities from the context.

Until at this point, we were able to stub or mock the DbContext and DbSet  *because all the methods our code used, were overridable. This allowed our us or Moq to replace their behavior in the tests. However, not all public members (or their dependencies) of *DbContext are like this, therefore it gets more difficult isolate the tests for some scenarios.

For example, if we would like to test the code that executes when an entity is read from the database, we would need to be able to raise the ObjectMaterialized event on the stubbed context. This event is not on the DbContext, but on the ObjectContext. The ObjectCotext property is nor public nor overridable, which makes it almost impossible to replace it with a stubbed ObjectContexton which we could trigger the event. To overcome this we can create a DbContext wrapper that just pushes up this event. Like this:

 public sealed class DbContextWrapper : IDbContextWrapper  
 {  
     private readonly ObjectContext objectContext;

     public DbContextWrapper(DbContext context)  
     {  
         Context = context;  
         objectContext = ((IObjectContextAdapter) context).ObjectContext;  
         objectContext.ObjectMaterialized += ObjectMaterializedHandler;  
     }

     private void ObjectMaterializedHandler(object sender, ObjectMaterializedEventArgs e)  
     {  
         EntityLoadedEventHandler handler = EntityLoaded;  
         if (handler != null)  
             handler(this, new EntityLoadedEventHandlerArgs(e.Entity));  
     }

     public DbContext Context { get; private set; }

     public event EntityLoadedEventHandler EntityLoaded;

     public void Dispose()  
     {  
         objectContext.ObjectMaterialized -= ObjectMaterializedHandler;  
         Context.Dispose();  
     }  
 }  

Now we need to modify all our test code and production code to use the IDbContextWrapper instead of the DbContext. The factory will return a stub for it and the stub can be configured to raise the event.

This is quite inconvenient. Our tests have too much knowledge of implementation details of the production code. Even more, when trying to tests more code that accesses data, things will get more complex and this wrapper will grow creating a hard to manage mess. It is also an example of how the tests may damage the production code design. Maybe by more refactoring this wrapper will lead to the IRepository interface as the abstraction of the repository pattern which hides EF from the production code, but… it seems unlikely and a very long an painful route.

All these point to the conclusion that for testing in isolation the business logic code, abstracting the data access in with some clear interfaces and testing from there up, is a better approach. Implementing the repository pattern on top of EF, not only gives better separation of concerns and may enforce consistency, but is also helps in testing.

However, testing on the DbContext directly may be useful when it is wanted to write some tests on the repository implementation that wraps the EF, and we want these tests to be isolated from the database and from the caller code. Such implementation is available on iQuarc github account, in the DataAccess repository.

More discussions about writing good unit tets are part of my Unit Testing Training
Featured image credit: geargodz via 123RF Stock Photo

Florin Coros

Read more posts by this author.