· entity framework ef ef6 c-sharp in-memory objects mock mock database repository testable unit test

Testable Data Access Layer using Entity Framework 6

Introduction

Unit testing is a great way to get some quick feedback on the code you have just implemented. Normally you can just write a unit test where you create an instance of a type and then invoke the method you want to test.
When you have a database involved you could do the same, however this creates a strong connection between your database and the unit test. One way of avoiding this dependency is to mock your database context.
To be able to mock your database context when using Entity Framework 6 Model First approach you need to make some modifications to the generated classes. In this guide I will show you how to make your data access layer testable.

This guide assumes you know how to set up a database and how to connect to your database from within Visual Studio.

If you just want to see the code, scroll to the bottom for a download link of the complete solution.

Generate the Data Model

Add the model to your solution:

Add New Item

Select ADO.NET Entity Data Model, name it Model.edmx, Choose Generate From Database

Connect to your database and:
Connect To Db

In the next screen select Entity Framework 6.0.
Then you must configure how the types in the model will be generated:

Choose Db Ojects

Set a check mark in the box for Tables and in the Pluralize or singularize generated object names checkbox and click Finish.
The above will create the objects required to interact with the database and add references to the relevant EF components to the current project in Visual Studio. It will also generate an App.config file where the configuration required by EF will be inserted.
Most relevant for this guide; templates for class generation is also added to our project.

Wrapping the Generated Classes in Interfaces

This guide’s focus is to make use of interfaces, which enables us to mock the database. To do so we must manipulate with the generated classes. Now we could just type the changes directly in the generated classes, but as I am sure you are aware of these changes will be lost if/when we make changes to our model and then update from the model.
Luckily, EF uses some text templates to generate the required classes and these templates can be modified to suite our needs. By modifying these templates, we will keep our changes, even if we update our model from the database.
As I mentioned previously, the templates are already added to the project. Look for the generated Model.edmx file and expand this in the Solution Explorer.
Now you should see a Model.Context.tt and a Model.tt file:
Solution Explorer TextTemplates

Basically what we need to do is extract an interface of the Model.Context.cs and make Model.Context.cs implement this interface.
In Model.Context.cs: Right-click the class name, select Refactor, select Extract, select Interface and mark the DbSet corresponding to the tables in your database. Next we need to change the interface so that all DbSet are changed to IDbSet.
Now we will modify the Text Templates.

Model.Context.tt

On line 56 add

, I<#=code.Escape(container)#>

so the full line is:

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext, I<#=code.Escape(container)#>

What did we just add? After the DbContext we assigned an interface of the name I*NameOfTheContextClass* which corresponds to the name of the interface generated when we refactored our model context.
On line 311, change the type of DbSet to IDbSet:

"{0} virtual IDbSet<{1}> {2} {{ get; set; }}",

Here we simply just changes the type to an interface type just as we did in our refactored interface.
Save the .tt file and Visual Studio should automatically generate the files again and if you open the Model.Context.cs it should look something like this:

public partial class EfRepositoryGuideEntities : DbContext, IEfRepositoryGuideEntities
    {
        public EfRepositoryGuideEntities()
            : base("name=EfRepositoryGuideEntities")
        {
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }
    
        public virtual IDbSet<Artist> Artists { get; set; }
        public virtual IDbSet<Track> Tracks { get; set; }
    }

Creating the unit test project

We need to make our mock of the ModelContext before we can write our tests. We also need to implement a fake DbSet which we can use when testing.
To your unit test project, add a class and implement it like the this:

class TestDataContext : IEfRepositoryGuideEntities
    {
        public IDbSet<Artist> Artists { get; set; }
        public IDbSet<Track> Tracks { get; set; }

        public TestDataContext()
        {
            Artists = new FakeDbSet<Artist>();
        }
    }

Now add a FakeDbSet class:

public class FakeDbSet<T> : IDbSet<T> where T : class
    {
        readonly ObservableCollection<T> data;
        readonly IQueryable query;

        public FakeDbSet()
        {
            data = new ObservableCollection<T>();
            query = data.AsQueryable();
        }

        public virtual T Find(params object[] keyValues)
        {
            throw new NotImplementedException("Derive from FakeDbSet<T> 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 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();
        }
    }

In this test we are only using in-memory objects which we inject on runtime. No connection is made to the database, there are no connection string in the unit test projects app.config and this makes our test totally independent of the database and gives us the control of what data we want our test to work with.

I have zipped the solution and please feel free to download it. It is complete with both the console application using the real database and the unit test project using in-memory objects. Of course the Data layer is also included. Please note that if you intend to try the console application, you need to setup a database on your local machine.

Download the source here!

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket