Thursday, 19 August 2010

Maintenance-Free Mocking for Unit Testing with Entity Framework 4.0

If you are trying to unit test in Entity Framework 4.0, you may have come across an apparent lack of architectural support for unit testing integration. Visual Studio 2010 appears to have excellent support for integrating Unit Test Projects into your solution, but no out-of-the box integration for using an Entity Framework Data Model (EDMX) generated Object Context is provided.

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 Synopsis

If 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 ObjectContext class 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 Problem

The issue with attempting to "drop in" a mocking approach in Entity Framework is that your specialised ObjectContext class, 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 MyObjectContext derives 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 Solution

The 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 ObjectContext and EntityObject classes 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:

Note that MyObjectContext now implements 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 Instructions

The download link and (very simple) guide to using is available here.


Thoughts and Considerations

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

ObjectQuery<T> Include
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.

27 comments:

  1. Thank you for this article and template.

    I tried to use them in the context of a RIA Services application. I'm facing a problem with the generated POCO entity classes.
    When RIA Services wants to generate the DomainContext on the client side, it complains that the POCO entity classes does not have a property identified as the key.

    Message: "The entity 'Person' in DomainService 'PersonManagerDomainService' does not have a key defined. Entities exposed by DomainService operations must have at least one public property marked with the KeyAttribute."

    Is it possible to modifiy the template to identify the key of an entity from the EDM and mark its property with [Key] as in the following code?

    [Key]
    public virtual decimal PersonId {get; set;}

    Thanks.
    Adriano

    ReplyDelete
  2. Hello again,

    I found a solution to my problem. I hope this may help someone.

    In the T4, replace
    WriteHeader(fileManager);
    by
    WriteHeader(fileManager, new String[] {"System.ComponentModel.DataAnnotations"});

    Then add the 4 following lines before the last line:
    <#
    if (ef.IsKey(edmProperty))
    #>
    [Key]
    <#=PropertyVirtualModifier(Accessibility.ForProperty(edmProperty))#> <#=code.Escape(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>

    Adriano

    ReplyDelete
  3. Thanks Adriano. There are a couple of funny things like this in using POCO classes. For instance using POCOs in WCF Data Services require disabling proxies, so the virtual modifiers have to be stripped from the properties.

    For the moment however, I've left the generation for the entities exactly the same way as the Microsoft ADO.NET POCO Generator does it. I think adding a [Key] attribute might stop it being a POCO, strictly speaking anyway.

    But thanks for your feedback. I hope someone else doing the same thing finds it useful.

    Rab

    ReplyDelete
  4. Hi
    By any chance do you have a sample in which we can test using the Database Connections as well as Mocking them at the sametime. I am getting heldup using these interfaces.

    Thanking you

    ReplyDelete
  5. Hi Ram -

    The interface adds the ability to have a mocking context. It still provides the ability to use your vanilla context that connects to the database. The mocking context is intended for use purely in unit testing your Business Logic Layer. The vanilla context is used to connect to the database, providing the database-side functionality.

    If you read the section above entitled Unit Testing & Mocking: A Brief Synopsis it tries to explain why unit tests on your Business Logic Layer shouldn't connect to database; a unit test is supposed to test only one "unit", not the full system.

    However, if you need to test your database, then you need a slightly different form of test. You need to use a Database Unit Test. Visual Studio 2010 provides the ability of performing these.

    Right click your Visual Studio Test Project, select Add -> New Test, and select Database Unit Test. This will use your ADO database connection to test functionality of database objects such as stored procedures and functions.

    If you want to perform tests on your code and your database together, then you need to put together a system test; I'm afraid a mocking context is not going to help you in this scenario.

    Regards
    Rab

    ReplyDelete
  6. Once I implemented ADO.Net Mocking Context Generator,my vanilla context.SaveChanges() doesn't work.

    I am fairly new to .net development,Am I doing it right?

    ReplyDelete
  7. Hi Anonymous -

    Possibly it's nothing down to anything you've done wrongly. Maybe I've messed up something in a later release.

    What happens? When you say that SaveChanges()doesn't work, what happens?

    Cheers
    Rab

    ReplyDelete
  8. It complains that IMyObjectContext does not contain a definition for 'SaveChanges'.

    For the non-mocked version, it comes from ObjectContext, which is not part of the interface, and is not used to make the Mock class.

    ReplyDelete
  9. Ah ok. All you need to do is downcast your interface to the concrete vanilla class. The interface doesn't advertise any EDMX-specific functionality.

    Sorry for slipping a sermon in, but you possibly could do with taking a step back to ensure that the logic functionality that you've implemented should really be calling SaveChanges at all. This sometimes points to a blurred line of responsibility.

    Can you maybe get the caller of your logic function, which already instantiates and therefore "knows" about your concrete ObjectContext type, to call SaveChanges() itself? Then you wouldn't need to downcast anything, and your logic would be nicely knitted up without any requirement for knowledge of the concrete ObjectContext implementations.

    Rab

    ReplyDelete
  10. so nice work, Rab

    BTW, how to test CUD function? your MockEntities don't have SaveChanges

    Jake

    ReplyDelete
  11. Sorry for the delay - No MockEntities doesn't have SaveChanges, use a derived reference and call SaveChanges there.

    ReplyDelete
  12. Great template!

    However, to me there seems to be a problem with the default interface generation (IMyObjectContext in your example):
    The generated interface does not implement IDisposable, which makes it impossible to have a using statement like
    using(var ctx=ResolveIMyObjectContext()) returning just the interface.
    What's the best solution for this?

    Regards
    Anders

    ReplyDelete
    Replies
    1. I've updated the T4 template to include the IDisposable implementation, more info available here: http://bogdanvarlamov.com/2013/02/27/entity-framework-mocking-template-with-idisposable-implementation/

      Delete
  13. What would be the DI pattern you would suggest while using these classes (+ MetaDataType T4 templates) in an MVC3 project?

    I know I want to lookup the ObjectContext (or mock) using DI but do I have to register all my (100s) types with something like unity? Can't get my head around the benefits of this over a simple class that checks a flag an returns either ObjectContext or mock? Any pointers?

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
  15. Thanx. This is pretty cool.

    I did notice one problem though. If you have a FK relationship between entities (for example: xxxTypeID to a xxxType table), when you try to delete a second entity from the ObjectSet... it just dies. No exception, no continuation, just death....

    ReplyDelete
  16. Wow this is amazing. I spent half a day trying to get the repository thing set up, read a ton of articles, got very confused and finally gave up.. The articles written by Microsoft are simply incomprehensible. But then I found this, a short and clear article and everything worked in 5 minutes!
    Actually I got the SaveChanges errors as well, but thinking about it I believe you are right, it should be moved to the instantiating classes.
    Now I hope this thing works well with database-first development and MOQ testing and I am set. It's almost too good to be true :)

    ReplyDelete
  17. Awesome, thanks for your work

    ReplyDelete
  18. Possible issue: http://stackoverflow.com/questions/9483296/many-to-many-relations-in-entity-framework-causes-infinite-loop#comment12037625_9483296

    ReplyDelete
  19. Now if we can just find a way to more easily stub out the fake data for the mock. Fake data stubs and object mother type stuff has always been the highest maintenance portion of our unit test portfolios.

    ReplyDelete
  20. This comment has been removed by the author.

    ReplyDelete
  21. This comment has been removed by the author.

    ReplyDelete
  22. As some people have already pointed out here (albeit implicitly), the use of the abstraction in the template is incorrect for many scenarios. I can't cast my concrete type to I because I doesn't inherit ObjectContext, and the rest of the framework expects it to be of type ObjectContext (in order for things like SaveChanges to work when generics are in play).

    A better solution would be to create an abstract class named Base that inherits from ObjectContext and the concrete and mock implementations would automatically implicitly inherit ObjectContext. Then the base type can be referenced without jumping through a bunch of hoops.

    All of this hype about "programming to an interface" seems to lead to confusion that an actual inteface is always the best way to go. However, sometimes (as in this case) an abstract class can do the job better.

    Great work on this template though, although the above problem exists currently, it should be pretty easy for me to fix. It would be nice if it were fixed in the original, though.

    ReplyDelete
    Replies
    1. Yikes, my post was altered because of the use of angle brackets to clarify things. Where it says "I", I meant "IMyEdmxType" and where it says "Base", I meant to say "MyEdmxTypeBase". Hopefully this clarifies it.

      Delete
  23. Ok, I made my fixes, but they weren't exactly what I described here. I don't see a link to the repository for a pull request, but my hope is that you review these changes and put them into the main project.

    See my post on stack overflow (Mock Entity Framework database) for the changes to the "Context" template. I didn't change the other one. http://stackoverflow.com/questions/6087857/mock-entity-framework-database/14406546#14406546

    ReplyDelete
  24. How to do function Import ? Is the template supports Function Imports?

    ReplyDelete
  25. Julie

    This is even better than Entity Framework:
    https://www.kellermansoftware.com/p-47-net-data-access-layer.aspx

    ReplyDelete