EF CTP4 Tips & Tricks: Testing With Fake DbContext
This post is based on a pre-release version of Entity Framework.
An updated post is available here.
This is the sixth in a series of posts about the recently released Entity Framework Feature CTP4, now affectionately known as “EF Magic Unicorn Edition”.
In this post we are going to take a look at how to replace a DbContext based data access layer with an in-memory fake for unit testing.
I’m going to use an MVC controller as an example but the same approach can be used for any component that consumes a derived DbContext, including repositories.
The Problem
Say we have a very simple model for Employees and Departments and we are using DbContext in Code First mode to persist and query our data:
public class EmployeeContext : DbContext
{
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int EmployeeId { get; set; }
public int FirstName { get; set; }
public int LastName { get; set; }
public int Position { get; set; }
public Department Department { get; set; }
}
In an MVC application we could add a controller that can display all departments in alphabetical order and allow us to add new departments:
(Typically we’d also implement ‘detail’, ‘edit’ and ‘delete’ functionality but I’m leaving them out because ‘list’ and ‘create’ are enough to demonstrate fakes)
public class DepartmentController : Controller
{
private EmployeeContext context;
public DepartmentController()
{
this.context = new EmployeeContext();
}
public ViewResult Index()
{
return View(this.context.Departments.OrderBy(d => d.Name).ToList());
}
public ViewResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Department dep)
{
context.Departments.Add(dep);
context.SaveChanges();
return RedirectToAction("Index");
}
}
The issue is that our controller now has a hard dependency on EF because it needs an EmployeeContext which derives from DbContext and exposes DbSets. We have no way to replace this with any other implementation and we are forced to run any unit tests against a real database, which means they really aren’t unit tests at all.
Adding an Interface
DbSet<T> happens to implement IDbSet<T> so we can pretty easily create an interface that our derived context implements:
public interface IEmployeeContext
{
IDbSet<Department> Departments { get; }
IDbSet<Employee> Employees { get; }
int SaveChanges();
}
public class EmployeeContext : DbContext, IEmployeeContext
{
public IDbSet<Department> Departments { get; set; }
public IDbSet<Employee> Employees { get; set; }
}
Now we can update our controller to be based on this interface rather than the EF specific implementation. You’ll notice I’m still using the EF implementation in the default constructor, in a real world app you would probably just have the interface based constructor and use dependency injection to supply the EF implementation at runtime, but I just wanted to keep things simple for this post
public class DepartmentController : Controller
{
private IEmployeeContext context;
public DepartmentController()
{
this.context = new EmployeeContext();
}
public DepartmentController(IEmployeeContext context)
{
this.context = context;
}
public ViewResult Index()
{
return View(this.context.Departments.OrderBy(d => d.Name).ToList());
}
public ViewResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Department dep)
{
context.Departments.Add(dep);
context.SaveChanges();
return RedirectToAction("Index");
}
}
Building Fakes
The first thing we need is a fake implementation of IDbSet<TEntity>, this is pretty easy to implement.
public class FakeDbSet<T> : IDbSet<T>
where T : class
{
HashSet<T> _data;
IQueryable _query;
public FakeDbSet()
{
_data = new HashSet<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public void Add(T item)
{
_data.Add(item);
}
public void Remove(T item)
{
_data.Remove(item);
}
public void Attach(T item)
{
_data.Add(item);
}
public void Detach(T item)
{
_data.Remove(item);
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
The one thing you’ll notice is that there isn’t really a good way to generically implement Find so I’ve left it as a virtual method that throws if called. If our application makes use of the Find method we can create an implementation specific to each type:
public class FakeDepartmentSet : FakeDbSet<Department>
{
public override Department Find(params object[] keyValues)
{
return this.SingleOrDefault(d => d.DepartmentId == (int)keyValues.Single());
}
}
public class FakeEmployeeSet : FakeDbSet<Employee>
{
public override Employee Find(params object[] keyValues)
{
return this.SingleOrDefault(e => e.EmployeeId == (int)keyValues.Single());
}
}
Now we can create a fake implementation of our context:
public class FakeEmployeeContext : IEmployeeContext
{
public FakeEmployeeContext()
{
this.Departments = new FakeDepartmentSet();
this.Employees = new FakeEmployeeSet();
}
public IDbSet<Department> Departments { get; private set; }
public IDbSet<Employee> Employees { get; private set; }
public int SaveChanges()
{ return 0; }
}
Testing Against Fakes
Now that we have our fakes defined we can use them to write a unit test for our controller, that doesn’t use EF:
[TestMethod]
public void IndexOrdersByName()
{
var context = new FakeEmployeeContext
{
Departments =
{
new Department { Name = "BBB"},
new Department { Name = "AAA"},
new Department { Name = "ZZZ"},
}
};
var controller = new DepartmentController(context);
var result = controller.Index();
Assert.IsInstanceOfType(result.ViewData.Model, typeof(IEnumerable<Department>));
var departments = (IEnumerable<Department>)result.ViewData.Model;
Assert.AreEqual("AAA", departments.ElementAt(0).Name);
Assert.AreEqual("BBB", departments.ElementAt(1).Name);
Assert.AreEqual("ZZZ", departments.ElementAt(2).Name);
}
Summary
In this post we saw how to build an interface that represents our context and how to build an in-memory fake of that context for use in our unit tests. We used an MVC controller to demonstrate this but the same approach can be used with any component that needs to interact with an EF based context, including repositories. There are a number of reasons to use in-memory fakes for unit testing but some key benefits are stable and robust tests that execute quickly and exercise a single component, making failures easy to isolate.



This is awesome,I was going to ask about this 2 days ago,good timing you posted this.
alaa9jo
September 7, 2010
Thanks
Price
September 8, 2010
Nice.. I like all your blog post.. Just added you to my favorite list.
Zeeshan Hirani
September 19, 2010
This is exactly what I was looking for. Thanks a lot for the post. BTW, I have a blog in portuguese and I´d like to ask you for permission to translate yours CTP4 series in it. May I?
Antonio Castro Jr
October 23, 2010
Yes, please feel free to translate any content!
romiller.com
October 26, 2010
Hi, love the post. I am having problems after doing this though creating a generic set. I.e. the below doesnt work anymore bacause IMyContext doesnt have set. Thanks for the great article.
private IMyContext context;
public IList List()
{
return context.Set().ToList();
}
ali
October 29, 2010
Hi,
You would just need to add a Set method to your interface, you probably want it to return an IDbSet rather than a DbSet so you would need to implement the method on you derived context:
public interface IEmployeeContext
{
IDbSet˂Department˃ Departments { get; }
IDbSet˂Employee˃ Employees { get; }
int SaveChanges();
IDbSet˂TEntity˃ Set˂TEntity˃() where TEntity : class;
}
public class EmployeeContext : DbContext, IEmployeeContext
{
public IDbSet˂Department˃ Departments { get; set; }
public IDbSet˂Employee˃ Employees { get; set; }
public new IDbSet˂TEntity˃ Set˂TEntity˃() where TEntity : class
{
return base.Set˂TEntity˃();
}
}
~Rowan
romiller.com
October 30, 2010
This tutorial is great, however I’m getting a compile error on the following. As far as I can tell, all of my interfaces and classes are set up according to your outline above, but for some reason the line below won’t compile. The error message is at the end. Any advice would be great, thanks and keep up the good work!
var context = new FakeContext
{
// This errors…
Categories = new Category { CategoryId = 1, CategoryName= “Test Category”, CategoryDescription=”Category Description”, SortOrder=1 }
};
Cannot implicitly convert type ‘Commerce.Data.Model.Category’ to ‘System.Data.Entity.IDbSet’. An explicit conversion exists (are you missing a cast?)
scott
November 2, 2010
Never mind… I found my problem… I needed to do this instead:
Categories = { new Category {CategoryId = 1, CategoryName= “Test Category”, CategoryDescription=”Category Description”, SortOrder=1 }}
scott
November 4, 2010
I have never used the MVC and completely new to the idea of fake.
Suppose we use the real data from the database and let the fake process it through a series of nested for loops, inserting the processed result into a table in the fake, all in the memory.
After we get the result can we transfer/persist the result back to the real database?
I am thinking of using this approach not so much for testing but that I think it is faster to insert into a table in the fake rather than making a few thousands of round trips to the database by inserting a row in the database after each successful step in a for loop using savechanges.
LW Ling
November 19, 2010
CTP 5 Version:
public class FakeDbSet : IDbSet
where T : class
{
readonly HashSet _data;
readonly IQueryable _query;
public FakeDbSet()
{
_data = new HashSet();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException(“Derive from FakeDbSet and override Find”);
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public void Detach(T item)
{
_data.Remove(item);
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
public TDerivedEntity Create() where TDerivedEntity : class, T
{
return Activator.CreateInstance();
}
public T Create()
{
return Activator.CreateInstance();
}
public ObservableCollection Local
{
get {
return new ObservableCollection(_data);
}
}
}
Lee Smith
December 9, 2010
@Lee – nice code, but you left out the T in the class declaration above.
public class FakeDbSet : IDbSet
Caused a bit of head scratching here for a while…
James
December 27, 2010
Oh – the T gets removed by the blog software!
James
James
December 27, 2010
Hi!
I am having a problem getting DbEntityEntry as the return type.
Can you help?
public System.Data.Entity.Infrastructure.DbEntityEntry Entry(T entity) where T : class
{
DbSetFake fakes = new DbSetFake();
fakes.FirstOrDefault();
return null;
}
Newbie
January 29, 2011
I am having similiar issue with getting the DbEntityEntry back out from implementing this method:
DbEntityEntry Entry(T entity) where T : class;
Here is my IDbContext interface:
public interface IDbContext : IDisposable
{
IDbSet Products { set; get; }
IDbSet Customers { set; get; }
DbEntityEntry Entry(T entity) where T : class;
int SaveChanges();
}
Here is my implementations:
public class DbContextFake : IDbContext
{
public DbContextFake()
{
this.Products = new ProductSetFake();
this.Customers = new CustomerSetFake();
}
public IDbSet Products { set; get; }
public IDbSet Customers { set; get; }
public System.Data.Entity.Infrastructure.DbEntityEntry Entry(T entity) where T : class
{
??????????????
//not sure how to return a DbEntityEntry object from the entity of type T though..
//can anyone help please????
}
public int SaveChanges()
{
return 0;
}
public void Dispose()
{
return;
}
}
Thanks.
Christine Baker
January 30, 2011
CTP5 Version
public class FakeDbSet : IDbSet where T : class
{
readonly HashSet _data;
readonly IQueryable _query;
public FakeDbSet()
{
_data = new HashSet();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException(“Derive from FakeDbSet and override Find”);
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public void Detach(T item)
{
_data.Remove(item);
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
public T Create()
{
return Activator.CreateInstance();
}
public ObservableCollection Local
{
get
{
return new ObservableCollection(_data);
}
}
public TDerivedEntity Create() where TDerivedEntity : class, T
{
return Activator.CreateInstance();
}
}
PeterLin
February 2, 2011
the are filtered by the system.
PeterLin
February 2, 2011
Thanks, Peterlin!
But I think your reply does not solve the problem. Here is the problem:
I have a repository that has a method like this:
public bool Update(Product entity)
{
try
{
this.context.Products.Attach(entity);
this.context.Entry(entity).State = EntityState.Modified;
return true;
}
catch
{
return false; }
}
My Unit Test:
///
///A test for Update
///
[TestMethod()]
public void CanUpdateTest()
{
//the fake DbContext to be constructor injected into the real repository implementation.
DbContextFake fakeDbContext = new DbContextFake();
//the real target ProductRepository implementation that accepts a IDbContext: DbContextFake implements IDbContext as above.
ProductRepository target = new ProductRepository(fakeDbContext);
//add a new product to the fake context object graph
Product product=new Product(123);
product.Description = “Added Product”;
fakeContext.Products.Add(product);
fakeContext.SaveChanges();
//get that product out using the product id
Product updateProduct = fakeContext.Products.Find(product.Id);
//make a change
updateProduct.Description = “My updated product”;
bool expected = true;
bool actual = false;
//act: now update this poor product.
actual = target.Update(updateProduct);
//assert
Assert.AreEqual(expected, actual);
}
Where it blows up:
this.context.Entry(entity).State = EntityState.Modified;
The exception is : this.context.Entry is NULL.
The reason for this is because IDbContext or IEmployeeContext above does not contain a method for this:
DbEntityEntry Entry(T entity) where T: class
If you add this method into your IDbContext or IEmployeeContext and implement it in your DBContextFake : IDbcontext
The question is : how do you implement this method that would give you a return type of
DbEntityEntry within this method?
Christine Baker
February 2, 2011
Hi Christine, did you solve your issue with DbEntityEntry? I’m having the same problem.
Thanks,
Romina
October 26, 2011
Great post, I like this a lot better than many of the other EF code first repository pattern options I’ve seen online. One thing to note for the Find method, if you have a naming convention for IDs in your app like I do (I name all PKs “Id”), you can use reflection to create a standard Find that will work in most cases:
public virtual T Find(params object[] keyValues)
{
// Assume there is an Id property, or else throw. We could also look for properties with [DatabaseGenerated(DatabaseGenerationOption.Identity)] attributes…
try
{
Type tType = typeof(T);
PropertyInfo idProperty = tType.GetProperty(“Id”);
return this.SingleOrDefault(d => (int)idProperty.GetValue(d, null) == (int)keyValues.Single());
}
catch (ArgumentNullException ex)
{
throw new NotImplementedException(“Could not find a primary key by the name of ‘Id’. Derive from FakeDbSet and override Find.”);
}
}
Aaron Bird
March 9, 2011
Hi,I am very intrigued by your post,does it mean that the creation of Generic repository and Unitofwork is redundant,since we can mock the Repository using IDBSet? I would be grateful for a reply to my email in case I cannot find this post anymore
brix
April 22, 2011
Here’s the latest version based on EF4.1
class FakeDbSet : IDbSet where T : class
{
readonly HashSet _data;
readonly IQueryable _query;
public FakeDbSet()
{
_data = new HashSet();
_query = _data.AsQueryable();
}
public T Add(T entity)
{
_data.Add(entity);
return entity;
}
public T Remove(T entity)
{
_data.Remove(entity);
return entity;
}
public T Attach(T entity)
{
_data.Add(entity);
return entity;
}
public TDerivedEntity Create() where TDerivedEntity : class, T
{
return Activator.CreateInstance();
}
public T Create()
{
return Activator.CreateInstance();
}
public T Find(params object[] keyValues)
{
throw new NotImplementedException(“Derive from FakeDbSet and override Find”);
}
public ObservableCollection Local
{
get { return new ObservableCollection(_data); }
}
public IEnumerator GetEnumerator()
{
return _data.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
public Type ElementType
{
get { return _query.ElementType; }
}
public System.Linq.Expressions.Expression Expression
{
get { return _query.Expression; }
}
public IQueryProvider Provider
{
get { return _query.Provider; }
}
}
Randy Forbes
June 13, 2011
Somehow I had garbage in the buffer…here’s the correct EF4.1 version:
class FakeDbSet : IDbSet where T : class
{
readonly HashSet _data;
readonly IQueryable _query;
public FakeDbSet()
{
_data = new HashSet();
_query = _data.AsQueryable();
}
public T Add(T entity)
{
_data.Add(entity);
return entity;
}
public T Remove(T entity)
{
_data.Remove(entity);
return entity;
}
public T Attach(T entity)
{
_data.Add(entity);
return entity;
}
public TDerivedEntity Create() where TDerivedEntity : class, T
{
return Activator.CreateInstance();
}
public T Create()
{
return Activator.CreateInstance();
}
public T Find(params object[] keyValues)
{
throw new NotImplementedException(“Derive from FakeDbSet and override Find”);
}
public ObservableCollection Local
{
get { return new ObservableCollection(_data); }
}
public IEnumerator GetEnumerator()
{
return _data.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
public Type ElementType
{
get { return _query.ElementType; }
}
public System.Linq.Expressions.Expression Expression
{
get { return _query.Expression; }
}
public IQueryProvider Provider
{
get { return _query.Provider; }
}
}
Randy Forbes
June 13, 2011
I have build similar classes with few improvements:
- use of IHasKey interface for every BO, and has primary key for every BO as int Id – and we have generic Find method
public class Client : IHasId
{
[Key]
public int Id { get; set; } // from Interface
….
}
public T Find(params object[] keyValues)
{
foreach (var keyValue in keyValues)
{
var res = _context.FirstOrDefault(k => k.Id == (int)keyValue);
if (res != null)
return res;
}
return null;
}
- also we have simulated SaveChanges
public class FakeDataSet : IDbSet where T : class, IHasId
{
private List _context = new List();
private List _toAdd = new List();
private List _toRemove = new List();
private List _toAttach = new List();
public int Save() { ….. }
…
}
and
public class FakeDataContext
{
public int SaveChanges()
{
var countChanges = 0;
var properties = ReflectionHelper.GetPropertiesOfType(GetType(), typeof(IDbSet));
foreach (var propName in properties)
{
var ds = ReflectionHelper.GetFieldOrProperty(this, propName);
countChanges += (int)ReflectionHelper.RaiseMethod(ds, “Save”);
}
return countChanges;
}
…
}
I have still one problem – how to count changes if we add tree of BO into FakeContext.
Arek
June 13, 2011
There is a way to generically implement Find by passing a function to be executed in the constructor:
Func _findFunction;
public FakeDbSet(Func findFunction)
{
_findFunction = findFunction;
_data = new HashSet();
_query = Data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
return _findFunction.Invoke(keyValues);
}
Using your example, it can be used like this:
public class FakeEmployeeContext : IEmployeeContext
{
public FakeEmployeeContext()
{
this.Departments = new FakeDbSet(kv => this.Departments.SingleOrDefault(d => d.DepartmentId == (int)kv.Single()));
this.Employees = new FakeDbSet(kv => this.Employees.SingleOrDefault(d => d.EmployeeId == (int)kv.Single()));
}
public IDbSet Departments { get; private set; }
public IDbSet Employees { get; private set; }
public int SaveChanges()
{ return 0; }
}
Now there is no need to create a child class for each FakeDbSet.
Brad
July 31, 2011
I took a different approach:
public T Find(params object[] keyValues)
{
var finders = new Dictionary<Type, Func> {
{typeof(LookupTableType), x => x.LookupTableTypeID == (int)keyValues.First()},
{typeof(ApplicationSetting), x => x.Key == (string)keyValues.First()}
};
if( finders.ContainsKey(typeof(T)))
return _queryableSet.SingleOrDefault(finders[typeof (T)]);
throw new NotImplementedException(“Find method not implemented for type”);
}
Michael Ceranski
August 4, 2011
All,
Here is the easiest way I’ve found to implement the finders:
public class Repository
{
public virtual IEnumerable Find(Expression<Func> whereClause)
{
return _dbSet.Where(whereClause).ToArray();
}
}
Usage:
var people = new Repository();
people.Find(p => p.Id == 1);
brycebudd
August 16, 2011
Trying again with generics encoded: Hope this works better
public class Repository<TEntity>
{
public virtual IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> whereClause)
{
return _dbSet.Where(whereClause).ToArray();
}
}
Usage:
var people = new Repository<Person>();
people.Find(p => p.Id == 1);
brycebudd
August 16, 2011
If using DataAnnotations I’ve found a nice way to implement find.
Find Method:
private List _keyProperties;
public virtual T Find(params object[] keyValues)
{
if (keyValues.Length != _keyProperties.Count)
throw new ArgumentException(“Incorrect number of keys passed to find method”);
IQueryable keyQuery = this.AsQueryable();
for (int i = 0; i _keyProperties[x].GetValue(entity, null).Equals(keyValues[x]));
}
return keyQuery.Single();
}
private void GetKeyProperties()
{
_keyProperties = new List();
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (PropertyInfo property in properties)
{
foreach (Attribute attribute in property.GetCustomAttributes(true))
{
if (attribute is KeyAttribute)
{
_keyProperties.Add(property);
}
}
}
}
brentmckendrick
November 30, 2011
Helpful Post. Thanks!
Jimmy Bosse
December 5, 2011
Thank you for the implementation in the comments. I have created a gist that add back the that were filtered out. Hope it saves somebody some time
https://gist.github.com/1438893
scarab5
December 6, 2011
[...] If you want to do unit testing with Entity Framework 4.2, I suggest you read this article: http://romiller.com/2010/09/07/ef-ctp4-tips-tricks-testing-with-fake-dbcontext/ [...]
How to do unit testing with EF 4.2
February 8, 2012
Great, thanks for posting this. it helped out more than you would know.
JR
My Site
May 2, 2012