I am creating a mock IDbSet to allow unit testing of entity framework classes, amongst other things.
However I am really having trouble detecting changes, or even figuring out how to do it at all. Here are my classes so far...
public interface IReportContext
{
IDbSet<Report> Reports {get;}
public int SaveChanges();
}
public class MockReportContext : IReportContext
{
IDbSet<Report> Reports {get;}
public int SaveChanges()
{
//Need to detect changes here???
}
public MockReportContext()
{
Reports = new MockDbSet<Report>();
}
}
public class MockDbSet<T> : IDbSet<T>
{
readonly ObservableCollection<T> _data;
readonly IQueryable _query;
public FakeDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public FakeDbSet(ObservableCollection<T> data)
{
_data = data;
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException();
}
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 T Detach(T item)
{
_data.Remove(item);
return item;
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public ObservableCollection<T> Local
{
get { return _data; }
}
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();
}
}
This works fine for adding, deleting and retrieving entities. However when I try the following:
IReportContext context = new MockReportContext();
context.Reports.Add(new Report()); //Works
Report report = context.Reports.First(); //Works
report.Message = "Hello World!";
context.SaveChanges(); //Does nothing
How is it the MockReportContext could know that the report object it returned has changed?? I know using entity framework does this so it must be possible, but I haven't got a clue how...
I think you're mostly there, but I'd suggesting using a mocking framework like Moq (my personal preference) or Rhino Mocks to mock the
IReportContext
for your unit tests, instead of going to the trouble of creating a Fake class likeMockReportContext
. (Alright, learning a mocking framework does involve some trouble, but it'll save a lot of Fake class drudgery down the road.)I would presume that you're unit testing the code that depends on the
IReportContext
, so you don't need to do anything insideSaveChanges()
, you just need to assert that your code did callSaveChanges()
internally if it was supposed to.Here's a good overview of using Moq with Entity Framework's derived DbContext / DbSet classes.
In the above linked overview, if the unit test at the end was testing a method that internally calls
SaveChanges()
, it could additionally verify thatSaveChanges()
was indeed called by your method with the line:You could also do this with your
MockReportContext
class by setting a side property to True inside theSaveChanges()
class and checking it at the end of your unit test, but a mocking framework is much more flexible and will save the need to write additional classes for unit tests.If you'd like to avoid using a mocking framework, then here's how to do it with fakes.