There’s an interesting change in EF6 that simplifies unit testing when EF is in the way and you don’t want to engage it at all.
EF6 DbSet gained new features. The team had to decide if they would make a breaking change to the existing IDbSet interface or leave that be and just change DbSet. They chose
the latter route. In doing so, they also ensured that we could use the DbSet directly for testing by adding a new constructor.
Here’ you can see the different constructors and how they affect our ability to test.
EF5 DbSet Constructor
The DbSet constructor is tied to a DbContext by way of the InternalQuery that is used internally in the constructor.
internal DbSet(InternalSet<TEntity> internalSet) : base((IInternalQuery<TEntity>) internalSet) { this._internalSet = internalSet; }
In EF5, we also have IDbSet (DbSet derives from this) (and IObjectSet which was introduced in EF4) . These interfaces contain the set operations (Add, Update, Remove and some additional methods through other interfaces) and can be implemented without forcing any ties to EF’s DbContext.
That’s what we’ve used in the past to create fake DbSets for testing scenarios.
EF6 DbSet Constructors
The internal constructor is still there.
internal DbSet(InternalSet<TEntity> internalSet) : base((IInternalQuery<TEntity>) internalSet) { this._internalSet = internalSet; }
But now there is another constructor. It’s protected and only uses an set interface, but not the query interface. This allows mocking frameworks to get access to DbSet and at the same time, benefit from some of the methods added to DbSet for EF6.
/// <summary> /// Creates an instance of a <see cref="T:System.Data.Entity.DbSet`1"/> when called from the constructor of a derived /// type that will be used as a test double for DbSets. Methods and properties that will be used by the /// test double must be implemented by the test double except AsNoTracking, AsStreaming, an Include where /// the default implementation is a no-op. /// /// </summary> protected DbSet() : this((InternalSet<TEntity>) null) { }
Even if you wanted to create your own fakes (or test doubles) in EF6, you can do that with DbSet now, not IDbSet. IDbSet is still there for backwards compatibility.
There are two detailed documents on MSDN for using EF6 to create Test Doubles and to use with Mocking Frameworks.
You also might find the meeting notes about this change interesting. I sure do! 🙂
I am curious to revisit my work with Telerik’s JustMock. I built some tests with EF5 and JustMock in my Automated Testing for Fraidy Cats course on Pluralsight. When using the paid version, everything just works. But when using JustMock Lite, the free version, it was not able to grok DbSets and you still needed to implement your own fake. I’ll be checking to see if the new DbSet implementation allows the free version of JustMock to mock DbSets on it’s own now.
//update about 20 minutes after initial post. The limitation of JustMock Lite is that it doesn’t support ReturnsCollection which is what you want to emulate the return of a DbSet. So if you’re not willing to pay for your tools, you can use the free version (which has a ton of features) and do a little extra work (create your own test double for DbSet which you can see how to do in MSDN doc I linked to above.
Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!
Hi Julie,
Thank you for your article – good stuff as always.
The "create Test Doubles" link is broken, is this the correct one?
msdn.microsoft.com/…/dn314431.aspx
Thanks,
Rui
Thanks Rui. Not sure what happened there but I’ve fixed. I love crowd-sourced copy edit!
This is a super article on Ef6. These new feature were much needed.
I've been following the guide at msdn.microsoft.com/…/dn314429.aspx using Moq. For whatever reason I can't mock anything that uses the .Include() method, I just get an object null exception. Anyone have hints on how to mock .Include() in EF6 using the new DbSet<>?
Hi,
I am facing issue with SqlFunctions.StringConvert method. It seems like it doesnot work after after EF-6 upgade. And it is working fine in previous version. Should i change the namespace or something?
exception:
LINQ to Entities does not recognize the method ‘System.String StringConvert(System.Nullable`1[System.Double])’ method, and this method cannot be translated into a store expression.
Thanks
Hi Sreekanth,
Yes it’s a namespace change. EF classes/namespaces that were in System.Data (but not under System.Data.Entity) moved.
The SqlFunctions were in System.Data.Objects.SqlClient.SqlFunctions.
They are now in System.Data.Entity.SqlServer.SqlFunctions.
I resolved it. Thanks a lot..
Great info! Julie, how would your implementation of the code in your Pluralsight course “Entity Framework in the Enterprise” simplify if re-tooled for EF6 to take advantage of this? Any details would be mundo appreciated.
I am starting to think that when I get the DDD and EF6 courses done, I will do a new version of the enterprise course. WRT the Mocking DBSets, I would get rid of the fake implementations and just use mocking. I would probably still demonstrate how to create fakedbsets etc for folks who just don’t want to use a mocking framework.
Hi,
Thanks for the article and thanks for great Pluralsight courses. I’m trying to test with Async queries using Moq, anything that uses .ToListAsync works but .FirstOrDefaultAsync throws an error: The member ‘IQueryable.Provider’ has not been implemented on type ‘DbSet`1Proxy_1’ which inherits from ‘DbSet`1’.
Any thoughts, thanks
sorry I don’t know. 🙁 A quick google search led me nowhere. Maybe try asking on stackoverflow? If you find the answer, would you mind adding a link here for any others that find this comment when searching the same problem. Thanks!
you can disable the proxycreation for EF
public class BloggingContext : DbContext
{
public BloggingContext()
{
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet Blogs { get; set; }
public DbSet Posts { get; set; }
}
https://msdn.microsoft.com/en-us/data/jj592886.aspx
I have read your entity framework and its amazing!! Thank you for that and You are doing great job via writing books and blog posts about entity framework.
I want to ask you a question. I think this may be off topic but sorry for that. I have implemented some code for entity framework 6.0 with TDD I want to know your opinion about it. Following is a link for sample code.
http://codereview.stackexchange.com/questions/47879/unit-of-work-and-repository-with-entity-framework-6
Do you think its a good or bad? Please let me know your feedback.
Thank you for posting these links. I’ve gotten the Mock working and have been able to write passing tests in basic Actions in the controller using Moq and xUnit but I’m having problems when the controller gets a bit more complex.
If I remember correctly you had written an article in MSDN a few years back on the use of WebGrid in which you linked to Stuart Leek’s article on using a service to handle paging and sorting. That ended up working great and I have used Stuart’s code in multiple controller actions in a production app. I’m now trying to retrofit that app with some unit tests on the controllers but whenever the paging and sorting service is called the test fails. I have a detailed post on stack overflow at the link below. Would you have any insight as to why calling a service would cause the test to fail?
Thank you,
Joe
http://stackoverflow.com/questions/23285549/moq-unit-test-fails-when-calling-a-service
Resolved. I was failing to initialize the service in the constructor when passing the mocked DB into the controller. Your readers can take a look at the stackoverflow post for an example of how to setup a test on a complex controller.
🙂
Hello Julie,
I’ve moved my database from SimpleMembership to Identity 2.0 and it broke my previously working test. Do you have any ideas on how to get the mock to work with Identity 2.0. Write here:
http://stackoverflow.com/questions/23484368/moq-mocking-with-identity-2-0-database
Thanks,
Joe
Hey Joe,
I’ve posted this on twitter. Maybe someone helpful will swing by! 🙂
Thanks Julie,
Twitter magic seems to have done the trick in that I got a response with the correct answer to my stackoveflow post. In short you need to add a call to the base class implementations when instantiating the mock.
var mockMyDb = new Mock() { CallBase = true };
when i use a generic repository and use
DbContext.Set()
to query entities, it cannot be mocked as Set is not virtual. Is there a work around other than the one I have mentioned here?For now i have sort of hacked my custom dbcontext classes. This allows me to mock IDbContext ( which has methods for all DBcontext properties and method I use for my project)
public interface IDbContext : IDisposable
{
DbSet Set() where TEntity : class;
int SaveChanges();
System.Data.Entity.Infrastructure.DbEntityEntry Entry(object entity);
}
And
public class MyCustomDbContext : System.Data.Entity.DbContext, IDbContext
{
….
}
Hi Julie,
I’m having trouble testing an Include statement on EF6. I’m using fakes to generate a StubFakeDbSet but have no idea how to get the include working. Any pointers would be greatly appreciated. 🙂
Hi Julie,
I recently implemented quite a few amenities for JustMock when working with Entity Framework. You can check out the repo on GitHub.
With the new library, working with mock DbContexts feels just as natural as working with real DbContexts.
If you have any comments or ideas I’d be very happy if you open an issue on the project site.
Happy Holidays!
I’m using the class provided in this post (https://msdn.microsoft.com/en-us/data/dn314429) to test an async controller method and it does not seem to support LINQ queries where you are using .Project().To() with an AutoMapper.
Is there a way to specify a Mapper to TestDbAsyncQueryProvider or FakeDBSet?
This is the error thrown.
System.NotSupportedException: This function can only be invoked from LINQ to Entities.
Thanks!
sorry I don’t happen to know.
Awesome article. Led me to write a little library to facilitate EF mocking in a simple way:
http://www.22bugs.co/post/Mocking-DbContext/
Great article, i would like to expose my nuget package about tests with mocks on EF 6.
http://www.codeproject.com/Tips/1045590/Testing-with-mock-on-Entity-Framework
I created it with this article (and another on msdn) as base.