How EF6 Enables Mocking DbSets more easily

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.

20 thoughts on “How EF6 Enables Mocking DbSets more easily

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

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

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

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

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

    1. 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!

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

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

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

  6. 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 };

  7. 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
    {
    ….
    }

  8. 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. :-)

Leave a Reply

Your email address will not be published. Required fields are marked *


3 × one =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>