Mocking EntityFramework context – Code First

Mocking EntityFramework context – Code First

mocking_ef
Source

When I first try to write test for class that use directly, I found that I can’t mock database context directly using for example Moq library. That’s because this class properties isn’t virtual. After some research I found that EF6 could be set up to enable mocking [1,2]. However that is not quite what I need. I had some specific requirements:

  1. I want to trace operations into database tables
  2. It must work for Code First EF6 configuration
  3. It should be generic solution for each database table

Let’s try to satisfy first requirement. We need some method to store all data our mocked object. I decided to write in-memory DbSet object like in [2] link.

public class FakeDbSetKeyed<T, TKey> : DbSet<T>, IQueryable, IEnumerable<T>
    where T : class, IKey<TKey>
{
    Dictionary<TKey, T> _data;
    IQueryable _query;
    public FakeDbSetKeyed()
    {
        _data = new Dictionary<TKey, T>();
        _query = _data.Values.AsQueryable();
    }

    public override T Find(params object[] keyValues)
    {
        return _data[(TKey)keyValues.First()];
    }

    public override T Add(T item)
    {
        _data.Add(item.Id, item);
        return item;
    }

    public override T Remove(T item)
    {
        _data.Remove(item.Id);
        return item;
    }

    public override T Attach(T item)
    {
        if (_data.ContainsKey(item.Id))
        {
            _data[item.Id] = item;
        }
        else
        {
            _data.Add(item.Id, item);
        }
        return item;
    }

    public T Detach(T item)
    {
        _data.Remove(item.Id);
        return item;
    }

    public override T Create()
    {
        return Activator.CreateInstance<T>();
    }

    public override TDerivedEntity Create<TDerivedEntity>()// where TDerivedEntity : class, T
    {
        return Activator.CreateInstance<TDerivedEntity>();
    }

    public override ObservableCollection<T> Local
    {
        get { return new ObservableCollection<T>(_data.Values); }
    }

    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.Values.GetEnumerator();
    }

    public void Clear()
    {
        _data.Clear();
    }
}

You can see two addition to original class definition from MSDN. I added also one additional method (Clear) for purpose of easier operate data sets during testing and also I added generic parameter TKey to specify key type. That enables me to write Find method.

Next, we consider second point of requirement – Code First compatibility. In mentioned MSDN articles we can set up mock for DbContext, but for Code First approach there isn’t work. That’s because during the creation of Mock parameterless constructor is executed. Thus the database could be created each time when we start single test and it is not acceptable and very time consuming. To solve it, I must create fake DbContext.

Let’s suppose that we have following DbContext

public class ItemsContext : DbContext
{
    public ItemsContext()
        : base("DatabaseName")
    {
    }

    public virtual DbSet<User> Users { get; set; }

    public virtual DbSet<Item> Items { get; set; }
}

This context isn’t ready to support its different implementation yet. So I defined interface IItemsContext with sets defined in main context class. I also implement IItemsContext in ItemsContext and change application to use interface beside of specific class.

public interface IItemsContext
{
    DbSet<User> Users { get; set; }
    DbSet<Item> Items { get; set; }
}

public class ItemsContext : DbContext, IItemsContext
{
    public ItemsContext()
        : base("DatabaseName")
    {
    }

    public virtual DbSet<User> Users { get; set; }
    public virtual DbSet<Item> Items { get; set; }
}

Thanks to this modifications in program structure I can write test initializations just like this:

[TestInitialize]
public virtual void TestInitialize()
{
    _dataSet = new FakeDbSetKeyed<TEntity, TKey>();
    var mockContext = new Mock<IBwEntities>(MockBehavior.Strict
    mockContext.Setup(c => c.Set<TEntity>()).Returns(_dataSet);
    _dataSet.Clear();
}

With this set up we fill _dataSet and operate on DbContext (ItemsContext for us) in tests in order to check _dataSet for the results of tested actions.

[1] https://msdn.microsoft.com/en-us/data/dn314429.aspx
[2] https://msdn.microsoft.com/en-us/data/dn314431.aspx