Unit testing with EF4 "Code First" and Repository

Solution 1:

You create a DbSet through the Factory method Set() on the Context but you don't want any dependency on EF in your unit test. Therefore, what you need to look at doing is implementing a stub of DbSet using the IDbSet interface or a Stub using one of the Mocking frameworks such as Moq or RhinoMock. Assuming you wrote your own Stub you'd just add the WeightEntry objects to an internal hashset.

You may have more luck learning about unit testing EF if you search for ObjectSet and IObjectSet. These are the counterparts to DbSet prior to the code first CTP release and have a lot more written about them from a unit testing perspective.

Here's an excellent article on MSDN that discusses testability of EF code. It uses IObjectSet but I think it's still relevant.

As a response to David's comment I'm adding this addendum below as it wouldn't fit in the -comments. Not sure if this is the best practice for long comment responses?

You should change the IWeightTrackerContext interface to return an IDbSet from the WeightEntries property rather than a DbSet concrete type. You can then create a MockContext either with a mocking framework (recommended) or your own custom stub. This would return a StubDbSet from the WeightEntries property.

Now you will also have code (i.e Custom Repositories) that depend on the IWeightTrackerContext which in production you would pass in your Entity Framework WeightTrackerContext that would implement IWeightTrackerContext. This tends to be done through constructor injection using an IoC framework such as Unity. For testing the repository code that depends on EF you would pass in your MockContext implementation so the code under test thinks it's talking to the "real" EF and database and behaves (hopefully) as expected. As you have removed the dependancy on the changeable external db system and EF you can then reliably verify repository calls in your unit tests.

A big part of the mocking frameworks is providing the ability to verify calls on Mock objects to test behaviour. In your example above your test is actually only testing the DbSet Add functionality which shouldn't be your concern as MS will have unit tests for that. What you'd want to know is that the call to the Add on DbSet was made from within your own repository code if appropriate and that is where the Mock frameworks come in.

Sorry I know this is a lot to digest but if you have a read through that article a lot will become clearer as Scott Allen is a lot better at explaining this stuff than I am :)

Solution 2:

Building on what Daz said, (and as I mentioned in his comments), you are going to need to make a 'fake' implementation of IDbSet. An example for CTP 4 can be found here but to get it to work you'll have to customize your Find method and add return values for a couple of the previously void-ed methods, like Add.

The following is my own crafted example for CTP 5:

public class InMemoryDbSet<T> : IDbSet<T> where T : class
{
    readonly HashSet<T> _data;
    readonly IQueryable _query;

    public InMemoryDbSet()
    {
        _data = new HashSet<T>();
        _query = _data.AsQueryable();
    }

    public T Add(T entity)
    {
        _data.Add(entity);
        return entity;
    }

    public T Attach(T entity)
    {
        _data.Add(entity);
        return entity;
    }

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
    {
        throw new NotImplementedException();
    }

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

    public virtual T Find(params object[] keyValues)
    {
        throw new NotImplementedException("Derive from FakeDbSet and override Find");
    }

    public System.Collections.ObjectModel.ObservableCollection<T> Local
    {
        get { return new System.Collections.ObjectModel.ObservableCollection<T>(_data); }
    }

    public T Remove(T entity)
    {
        _data.Remove(entity);
        return entity;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    public Type ElementType
    {
        get { return _query.ElementType; }
    }

    public Expression Expression
    {
        get { return _query.Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _query.Provider; }
    }
}