Need to Mock a DbSet for testing Entity Framework?
I wanted to create fast unit tests that did not rely on the database, nor data in the DB. I used the Moq framework to create mock objects, but the Entity Framework DbSet was not easy to mock, so I created my own class. The gist is on github. View the readme in the gist for more details. (One of the hardest things is to workaround the DbFunctions which only work for LINQ to Entities).
I have used this code and it works well, but if you arre using DbFunctions, those are impossible to test with mocks. My suggestion is to use this where it makes sense, but know that you will not get complete code coverage especially if you use DbFunctions or Execute any SQL commands directly.
Another thing to consider is to not use the ORM for large transactions. For large selects, it's ok, but if you are doing any large updates, inserts or deletes, consider side-stepping the ORM and go for a more performant solution.
- You might need to implement GetHashCode (return an Id) and Equals (compare Id's) in order for the HashSet within InMemorySet to update properly.
- There are other things you may need to do to get testing with Moq to work with Entity Framework.
- I use NUnit instead of MS Testing Framework since NUnit has
[TestCase()]
(as of 6, 2015) - Don't forget to use
mock.Object
. - Don't forget to setup your mocks like this
mockDb.Setup(x => x.Entities).Returns(entities);
where entities is anInMemorySet<TEntity>()
; - Using
new Mock<TEntity> { CallBase = true };
is very handy such that you do not need to "Setup
" all of the methods of the mocked object. mockEntity.SetupAllProperties();
is also convenient, such that you can assign to properties instead of calling "Setup().Returns
".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.Data.Entity; | |
using System.Linq; | |
using System.Linq.Expressions; | |
namespace FCM.Web.Tests.TestHelpers | |
{ | |
/// <summary> | |
/// This is awesome. It's a class you can use to Mock a DbSet for | |
/// testing Entity Framework using mocks. | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
class InMemorySet<T> : IDbSet<T> where T : class | |
{ | |
private HashSet<T> _hashSet; | |
public InMemorySet() | |
{ | |
_hashSet = new HashSet<T>(); | |
} | |
public Expression Expression { get { return _hashSet.AsQueryable().Expression; } } | |
public Type ElementType { get { return _hashSet.AsQueryable().ElementType; } } | |
public IQueryProvider Provider { get { return _hashSet.AsQueryable().Provider; } } | |
public T Find(params object[] keyValues) | |
{ | |
throw new NotImplementedException(); | |
} | |
public bool Contains(T entity) | |
{ | |
return _hashSet.Contains(entity); | |
} | |
public T Add(T entity) | |
{ | |
_hashSet.Add(entity); | |
return entity; | |
} | |
public void AddOrUpdate(params T[] entities) | |
{ | |
foreach (var entity in entities) | |
{ | |
AddOrUpdate(entity); | |
} | |
} | |
private void AddOrUpdate(T entity) | |
{ | |
if (_hashSet.Contains(entity)) | |
{ | |
// Update behavior | |
_hashSet.Remove(entity); | |
} | |
_hashSet.Add(entity); | |
} | |
public T Remove(T entity) | |
{ | |
_hashSet.Remove(entity); | |
return entity; | |
} | |
public T Attach(T entity) | |
{ | |
_hashSet.Add(entity); | |
return entity; | |
} | |
public T Create() | |
{ | |
throw new NotImplementedException(); | |
} | |
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T | |
{ | |
throw new NotImplementedException(); | |
} | |
public ObservableCollection<T> Local { get; private set; } | |
public IEnumerator<T> GetEnumerator() | |
{ | |
return _hashSet.GetEnumerator(); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return _hashSet.GetEnumerator(); | |
} | |
} | |
} |
Comments
Post a Comment