Faking DbContext in Entity Framework 4.1 with a generic repository

Update 30/11/2011: FakeDbSet implementation update Please see the new and improved FakeDbSet Here

Update 16/06/2011:  Added step (2) description of how to implement Set<>() method in your original DbContext so that it returns IDbSet<>. Also added SaveChanges() to expose the context as a unit of work. + A little reorganisation.

Faking of the new Entity Framework 4.1 DbContext can be done quite simply by following these steps:

1. Create a common interface for your particular DbContext type.

I’m using a generic repository so my interface only needs to implement the Set method. But you could of course expose all your collections through this interface.

    public interface IMainModuleContext
    {
        IDbSet<Person> People { get; set; } // My collections...
        IDbSet<TEntity> Set<TEntity>() where TEntity : class;
        void SaveChanges();
    }

Notice how our DbSet collections IDbSet instead of DbSet. This is because we will use an in-memory representation of the DbSet collection called FakeDbSet which implements IDbSet.

If you are exposing all of your collections and using model-first this could be generated with a T4 Template to save development time. Ensure your real DbContext implements this interface, and that your repository will take a IMainModuleContext instead of the concrete type.

2. Now lets make sure our original context (mine is called MainModuleContext) is implementing this interface. Example of the code to do this is below:

public partial class MainModuleContext : DbContext, IMainModuleContext
{
    public IDbSet<Person> People { get; set; }
    public MainModuleContext() : base() {}
    public IDbSet<TEntity> Set<TEntity>() where T : class
    {
        return base.Set<TEntity>();
    }

    public void SaveChanges()
    {
        base.SaveChanges();
    }
    // Other methods
}

Notice our properties must return IDbSet instead of DbSet. This is easy since the EF team have included the IDbSet interface for us.

3. Now we will create a fake dbset (an in-memory representation of a dbset)

    public class FakeDbSet<T> : IDbSet<T> where T : class
    {
        private HashSet<T> _data;

        public FakeDbSet()
        {
            _data = new HashSet<T>();
        }

        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 void Detach(T item)
        {
             _data.Remove(item);
        }

        Type IQueryable.ElementType
        {
            get { return _data.AsQueryable().ElementType; }
        }

        Expression IQueryable.Expression
        {
            get { return _data.AsQueryable().Expression; }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return _data.AsQueryable().Provider; }
        }

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

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

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

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

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

4. Now implement your fake context. The only tricky thing here is the Set method needs to use reflection to find the property we are after.


    public partial class FakeMainModuleContext : IMainModuleContext
    {
        public IDbSet<Person> People { get; set; }
        public IDbSet<T> Set<T>() where T : class
        {
            foreach (PropertyInfo property in typeof(FakeMainModuleContext).GetProperties())
            {
                if (property.PropertyType == typeof(IDbSet<T>))
                    return property.GetValue(this, null) as IDbSet<T>;
            }
            throw new Exception("Type collection not found");
        }

        public void SaveChanges()
        {
             // do nothing (probably set a variable as saved for testing)
        }

        public FakeMainModuleContext()
        {
            // Set up your collections
            People = new FakeDbSet
            {
                new Person() { FirstName = "Brent" }
            };
        }

You can now swap out your DbContext with a FakeDbContext for unit testing.

Advertisements

20 thoughts on “Faking DbContext in Entity Framework 4.1 with a generic repository

  1. I feel a little sleepy, so i can’t figure out exactly how you implemented Set.
    Specifically, MainModuleContext (the original dbcontext name in your case, i guess) implements Set by inheriting from DBContext. And that returns DbSet, not IDbSet.
    Compiler doesn’t appreciate this:

    ‘Blah.BusinessObjects.EntitiesContainer’ does not implement interface member ‘Blah.BusinessObjects.IEntitiesContainer.Set()’.

    ‘System.Data.Entity.DbContext.Set()’ cannot implement ‘Blah.BusinessObjects.IEntitiesContainer.Set()’ because it does not have the matching return type of ‘System.Data.Entity.IDbSet’

    • Hi saveliev,
      Sorry if I didn’t make it clear enough the post was quite rushed… Looks like I did forget to show the implementation of the ‘MainModuleContext’. I’ll add that in as an edit soon.

      EDIT: see edited post 🙂

  2. Hi, i’m using your approach but i changed something to my actual scenario, here is the code where I changed:

    public class FakeQuestiona2011Context : IQuestiona2011Context
    {
    private IDbSet _credencial;
    private IDbSet _perfil;
    private IDbSet _apurador;
    private IDbSet _entrevistado;
    private IDbSet _setor;
    private IDbSet _secretaria;
    private IDbSet _pesquisa;
    private IDbSet _pergunta;
    private IDbSet _resposta;

    public IDbSet Credencial { get { return _credencial ?? (_credencial = new FakeDbSet()); } set { } }
    public IDbSet Perfil { get { return _perfil ?? (_perfil = new FakeDbSet()); } set { } }
    public IDbSet Apurador { get { return _apurador ?? (_apurador = new FakeDbSet()); } set { } }
    public IDbSet Entrevistado { get { return _entrevistado ?? (_entrevistado = new FakeDbSet()); } set { } }
    public IDbSet Setor { get { return _setor ?? (_setor = new FakeDbSet()); } set { } }
    public IDbSet Secretaria { get { return _secretaria ?? (_secretaria = new FakeDbSet()); } set { } }
    public IDbSet Pesquisa { get { return _pesquisa ?? (_pesquisa = new FakeDbSet()); } set { } }
    public IDbSet Pergunta { get { return _pergunta ?? (_pergunta = new FakeDbSet()); } set { } }
    public IDbSet Resposta { get { return _resposta ?? (_resposta = new FakeDbSet()); } set { } }

    public void SaveChanges()
    {
    // do nothing (probably set a variable as saved for testing)
    }
    }

    In my controller i’m using context like:

    context.NameOfEntity.Add(…)

    In my test i set the registers in fakeContext and I test the Controller with this fakes registers.

  3. Here is the easiest way I’ve found to implement the Find method:

    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);

  4. Pingback: Brain Twist: .NET MVC 3, Entity Framework 4.1, and TDD

  5. Pingback: NUnit Testing of Entity Framework 4.1 and code-first | … the rest is just coding

  6. Pingback: Fake DbContext of Entity Framework 4.1 to Test | SeekPHP.com

  7. Pingback: Fake DbContext of Entity Framework 4.1 to Test

  8. Pingback: Generic Repository: Fake IDbSet implementation update (Find Method & Identity key) « REFACTOR (this)

  9. Hi, I tried what you proposed here, but using the DbContext Interface (in your example IMainModuleContext) results in NotSupportedExceptions (the one that goes “Unable to create a constant value of type …”) when using subqueries. I found this question on stackoverflow regarding the problem: http://stackoverflow.com/questions/5995574/interface-and-repository-abstraction-break-subqueries-in-entity-framework-4-1-db. The problem can be solved by storing the inner query in a local variable and use it in the outer query (described in the aforementioned question).
    I thought that might be helpful to point that out in this context. Or maybe there is another solution to this problem?

  10. Pingback: How to EF4 + Ninject + Moq? | PHP Developer Resource

  11. Thanks for your solution! I have one problem though…

    This works…
    var allWatchLists = _watchListRepository.AllIncluding(watchlist => watchlist.WatchListItems);
    var watchLists = allWatchLists.Where(item => item.UserId == null);

    But this does not…
    var allWatchLists = _watchListRepository.AllIncluding(watchlist => watchlist.WatchListItems);
    var watchLists = allWatchLists.Where(item => item.User.Email == “test@user.com”);

    Even though an allWatchLists.User with an Email = “test@user.com” does exist (I checked @ run-time).

    I see this same issue all over the web. Ladislav Mrnka is saying that referential integrity is broken. I though I took care of that by simply doing WatchList.Users.Add(User) and User.WatchList = User.

    Am I missing something?

    Thanks in advance for your help.

  12. Hi , I am using this approach to test the application using fake databse context.
    But the interface has method which deals with the Entity State(like SetEntityState() and RetrieveEntityState()) which works fine with real context due to DbEntityEntry implementation in DbContext class.

    My question here is How to fake the Entity State behavior in the fake database context class?

    Thanks in advance for your help
    Samba

  13. I really like this with t4 code generation- I can dynamically regenerate my fake context.

    Currently using Entity framework power tools for this with custom templates.

  14. Pingback: Using the Unit Of Work lifetime for datacontexthttp://blog.stevensanderson.com/2007/11/29/linq-to-sql-the-multi-tier-story/Setting | mockingcompetence

  15. When I see that you replace real DbSet with in memory dbset I read no longer. This doesnt make any sense people! Think! You will be able to create any query against in memory db set. But those queries will fail when you will be using real DbSet in your application. Does any of you see the difference between LINQ to Objects and LINQ to Sql!?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s