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 specialisedObjectContext
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 specialisedObjectContext
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 EntityObjectYou 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.
Thank you for this article and template.
ReplyDeleteI 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
Hello again,
ReplyDeleteI 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
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.
ReplyDeleteFor 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
Hi
ReplyDeleteBy 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
Hi Ram -
ReplyDeleteThe 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
Once I implemented ADO.Net Mocking Context Generator,my vanilla context.SaveChanges() doesn't work.
ReplyDeleteI am fairly new to .net development,Am I doing it right?
Hi Anonymous -
ReplyDeletePossibly 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
It complains that IMyObjectContext does not contain a definition for 'SaveChanges'.
ReplyDeleteFor the non-mocked version, it comes from ObjectContext, which is not part of the interface, and is not used to make the Mock class.
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.
ReplyDeleteSorry 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
so nice work, Rab
ReplyDeleteBTW, how to test CUD function? your MockEntities don't have SaveChanges
Jake
Sorry for the delay - No MockEntities doesn't have SaveChanges, use a derived reference and call SaveChanges there.
ReplyDeleteGreat template!
ReplyDeleteHowever, 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
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/
DeleteWhat would be the DI pattern you would suggest while using these classes (+ MetaDataType T4 templates) in an MVC3 project?
ReplyDeleteI 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?
This comment has been removed by the author.
ReplyDeleteThanx. This is pretty cool.
ReplyDeleteI 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....
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!
ReplyDeleteActually 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 :)
Awesome, thanks for your work
ReplyDeletePossible issue: http://stackoverflow.com/questions/9483296/many-to-many-relations-in-entity-framework-causes-infinite-loop#comment12037625_9483296
ReplyDeleteNow 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.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteAs 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).
ReplyDeleteA 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.
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.
DeleteOk, 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.
ReplyDeleteSee 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
How to do function Import ? Is the template supports Function Imports?
ReplyDeleteJulie
ReplyDeleteThis is even better than Entity Framework:
https://www.kellermansoftware.com/p-47-net-data-access-layer.aspx
Great job! Any plans on making this template work on VS2017? I have a huge legacy project which is using this approach massively and would be great to be able to migrate to newest IDE. Thanks.
ReplyDelete