Making Entity Framework More Unit-Testable

Abstract

For developers using Entity Framework, unit testing code that depends on the DbContext class is not the easiest thing. If we’re interested in doing unit testing, we need to be able to use an in-memory (“fake” in Martin Fowler’s terminology) version of DbContext comprised of lists of objects so that our tests do not hit the database. This series of posts takes you through my process in getting to a facade on top of Entity Framework that results in testable code and tests that are fast to write.

Note: The issues described are only known to apply to Entity Framework <= 6.x. EF 7 is a complete rewrite, and things are very different. See here for more details.

We have to change IMyDbContext along with MyDbContext every time we add a table to the database: We would have to add DbSet<T> properties to both the interface and the implementation. We also cannot reuse this code across projects because our interface is tightly coupled to the particular database schema we’re using.

Fortunately, DbContext exposes a Set<T>() method (MSDN Docs) which can help us solve this problem. So, if we change how we consume our DbContext from db.Orders to db.Set<Order>(), we can now define our interface like this:

The name change on the interface from IMyDbContext to IDataSession is intentional: it reflects that this interface is no longer a representation of a particular database context (i.e. MyDbContext), but any database context. And we can create an implementation for it like so:

We can use our DI container to give us an EntityDataSession with whatever DbContext we want, and we can reuse this code across projects. The code below is for StructureMap) but your DI container should have similar constructs:

Part 2 - Testing

We want to be able to write our tests with a minimum of incidental complexity. In other words, we want to be able to create an in-memory implementation of IDataSession, specifying the tables and entities we need for the test - no more, and no less. In order to provide a super-simple interface for our tests, we need to hide some complexity within our IDataSession wrapper.

Unfortunately, I was not able to accomplish this succintly without including a mocking library, albeit a very nicely written one). Any suggestions for improvement here would be greatly appreciated. The implementation follows:

Part 3 - Putting it All Together

Now that we have structured our client code so that it depends on interfaces instead of implementations, and we have an in-memory version of IDataStore, we can create tests that are fast to write, fast to execute, and test the necessary behavior of our program. Here’s an example of how we can do this:

Let’s suppose we have a business rule that states “customers on credit hold cannot place new orders” (because they haven’t paid their bills). That rule might give us a controller that looks like this:

Conclusion

Entity Framework can be a useful tool in the toolbelt, but it requires a little bit of work on top to keep your code testable. Doing that work is well worth the gains in the ability to write tests that are fast to write, fast to execute, and let you focus on the business logic of your application.

Happy coding!

UPDATE 2015-11-26: A Note on Entity Framework 7

I tried updating the code to use Entity Framework 7 and found that things are quite different, as one would expect with a complete rewrite. This isn’t the most informed advice as I’ve only worked with EF 7 for a few hours, but it looks like the problems this code solves are now covered by the EntityFramework.InMemory package.