This article discusses how to implement a Mocking based approach to Unit Testing in a way that requires zero future code maintenance; generated automatically from the EDMX. The implementation doesn't require anything more than Visual Studio 2010. The finished product is implemented as a self-contained Visual Studio 2010 Item Template, available for download (see bottom of article).
Unit Testing & Mocking: A Brief SynopsisIf you want to unit test a Business Logic Layer, the BLL mustn't access the database within any part of the test cycle. If this happens the tests could be "contaminated" with errors from other layers. Additionally this sort of approach, whilst valid for full system testing, increases the overhead of performing the tests; a test database has to be set up, the schema implemented and the data put into a known state. A unit test only deals with testing on the component level, and shouldn't be dependent on persistent data, nor should it persist data itself.
Mocking is the process of swapping a functional object with a simulated mock object which shares the same interface. In the context of a BLL, this is normally the DAL "facade" or manager class; like your specialised
ObjectContextclass in Entity Framework. This mock object may then be provided to the test client in the stead of the functional class, and instead of retrieving and persisting data to the database, the mock object will gather data and/or report for testing purposes.
The ProblemThe issue with attempting to "drop in" a mocking approach in Entity Framework is that your specialised
ObjectContextclass, the facade class generated from the EDMX, has no generated interface containing the specialised functionality. Without an interface, it's impossible to implement the classical approach to implementing a Mock ObjectContext:
From the class diagram shown you can see that
MyObjectContextderives directly from
ObjectContext; the only way to provide a Mock ObjectContext using the out-of-the-box architecture would be either a reflection or proxy-based approach, or by completely overriding the functionality in the base class (creating a refused bequest smell). All undesirable as they are complicated, potentially more error prone, and more difficult for a new developer on the team to pick up as they don't follow the classical Mocking approach.
The SolutionThe solution is very simple, thanks to the new T4 (Text Template Transformation Toolkit) integration provided in Entity Data Modeller in Entity Framework 4.0. For the uninitiated, T4 is what is already used to generate your specialised
EntityObjectclasses from your EDMX file behind the scenes.
By using T4 we can instead come up with the following generated architecture, with very few changes to the out-of-the-box solution:
IMyObjectContext. This allows the implementation of the
MockMyObjectContext, and for it to share the same interface. This interface is passed to business logic clients instead of the concrete contexts, so that they are unaware if they are talking to the database, or instead being unit tested.
Download and InstructionsThe download link and (very simple) guide to using is available here.
Thoughts and ConsiderationsChange Tracking and EntityObject
You may have noticed that the strongly typed entity object now derives from Object instead of EntityObject. This is because the entity object is now implemented as a POCO object as the complex change tracking interferes with the mocking. However, Entity Framework 4.0 employs generated proxy classes to manage change tracking if POCO objects' properties are implemented as virtual. See POCO in the Entity Framework.
In the mocking context, ObjectSet<T> can no longer be used as this is a concrete object that liaises with the database. The context interface instead generates collections of type IObjectSet<T>, and both the concrete functional context and the mocking context implement this.
However, because ObjectSet<T> derives from ObjectQuery<T> this means we lose Include. To provide a workaround, the T4 generator implements an extension class for the interface IQueryable, that provides an Include method. This should mean that you can continue to use your existing code without any changes.
The workaround used was taken from Julie Lerman's Blog.