Click here to Skip to main content
15,881,812 members
Articles / DevOps / Testing

How to Mock Test an Entity Framework Model-First Project

Rate me:
Please Sign up or sign in to vote.
5.00/5 (12 votes)
29 Aug 2012CPOL5 min read 55.9K   22   11
Explains how to mock test an EF Model-First project using ADO.NET Entity Data Model template

Introduction

In TDD, it is important to write your unit tests first to test code logic without relying on a database. Even later in the development life cycle, maintaining a DB just for unit testing could be both cumbersome and erroneous. In addition, having a DB for automated unit testing with a CI (continuous integrated) build process may introduce unnecessary complexity. This article provides an example to show how to mock test an EF Model-First project using ADO.NET Entity Data Model template to strengthen and simplify your unit test implementation.

Background

My current project uses .NET 4 + EF 4 with generated code from the ADO.NET Entity Data Model template (.edmx). In my research of a good way to mock test the code, I find most of the examples available are focused on Code First/POCO projects with very few mentioning Model-First ones. This is probably due to the generated code in Designer.cs and its lack of flexibility as a result of some oversights from MS to enable code to be mock tested.

Unfortunately, to overcome these problems means we'll need to make some changes to the generated code. However, since these changes are relatively easy to make, a script could be written to alleviate the pain of this repetitive task every time after you've updated the model.

To Get Started

Let's first have a very simple data model defined:

Image 1

Then let's create an .edmx file based on this model:

Image 2

Now you should have added the .edmx file and its generated code to your project. Let's take a look at the generated code and understand what the problems are.

Open up MyModel.Designer.cs and go to the class declaration and you should see a line like this:

C#
public partial class MyEntities : ObjectContext

The first problem is that this class inherits from ObjectContext by default. ObjectContext has an internal constructor which establishes a DB connection based on the connection string. Since this is an internal constructor, it can't be overridden by using either a partial class or defining a common interface. Hence, if we want to use an interface for our mock entity base class, we'll have to modify this generated code.

Next, the Customers entity collection is declared as:

C#
public ObjectSet<Customer> Customers

Since ObjectSet<T> is a concrete class, we'll need to change it to an interface that ObjectSet<T> implements in order to be able to mock it. In summary, we need to make the following changes to MyModel.Designer.cs:

  • Replace inheritance from ObjectContext to the common interface you define (explained below)  
  • Replace all ObjectSet<T> to IObjectSet<T>

Now that shouldn't be so bad. Let's move on to how we code our mock entity and interface.

Polymorphism is Your Friend

In order to have a mock entity class that has the same set of methods and be able to use it to do mock test code, we need a common interface defined for both the real and mock entity class.

C#
public interface IMyEntities
{
    IObjectSet<Customer> Customers { get; }
    IObjectSet<Order> Orders { get; }
    int SaveChanges();
}

Once we have this interface defined, we can have both of our real and mock entity class implement it. Here's what the mock entity class looks like:

C#
public class MyEntitiesMock : IMyEntities
{
    private IObjectSet<Customer> customers;
    private IObjectSet<Order> orders;
 
    public IObjectSet<Customer> Customers
    {
        get { return customers ?? (customers = new MockObjectSet<Customer>()); }
    }
 
    public IObjectSet<Order> Orders
    {
        get { return orders ?? (orders = new MockObjectSet<Order>()); }
    }
 
    public int SaveChanges()
    {
        return 0;
    }
}

Note that since both the real and mock entity class should implement the same IMyEntities interface, we'll need to do the first change aforementioned in the generated code in MyModel.Designer.cs:

C#
public partial class MyEntities : IMyEntities

In MyEntitiesMock, both public property Customers and Orders return an instance of MockObjectSet<T>, which we will explain later. Notice the method SaveChanges() really doesn't do anything except returning 0. You can have other skeleton methods here doing the same thing to mock those supported by EF.

The MockObjectSet<T> class has to implement all the methods defined in IObjectSet to provide the same functionality. Here's what it looks like:

C#
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Data.Objects;
 
namespace MyApplication
{
    public partial class MockObjectSet<T> : IObjectSet<T> where T : class
    {
        private readonly IList<T> collection = new List<T>();
 
        #region IObjectSet<T> Members
 
        public void AddObject(T entity)
        {
            collection.Add(entity);
        }
 
        public void Attach(T entity)
        {
            collection.Add(entity);
        }
 
        public void DeleteObject(T entity)
        {
            collection.Remove(entity);
        }
 
        public void Detach(T entity)
        {
            collection.Remove(entity);
        }
 
        #endregion
 
        #region IEnumerable<T> Members
 
        public IEnumerator<T> GetEnumerator()
        {
            return collection.GetEnumerator();
        }
 
        #endregion
 
        #region IEnumerable Members
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return collection.GetEnumerator();
        }
 
        #endregion
 
        #region IQueryable<T> Members
 
        public Type ElementType
        {
            get { return typeof(T); }
        }
 
        public System.Linq.Expressions.Expression Expression
        {
            get { return collection.AsQueryable<T>().Expression; }
        }
 
        public IQueryProvider Provider
        {
            get { return collection.AsQueryable<T>().Provider; }
        }
        
        #endregion
    }
}

The last thing we'll need is a context container to instantiate and return the proper object context. Again, we need to define a common interface so we can have two different container classes, each with its own behavior.

The context container, along with the real context class looks like this:

C#
namespace MyApplication
{
    public interface IContextContainer
    {
        IMyEntities Current { get; }
    }
 
    public class ContextContainer : IContextContainer
    {
        private static readonly IMyEntities objectContext = new MyEntities();
 
        public IMyEntities Current
        {
            get { return objectContext; }
        }
    }
}

The mock context container implements the same interface and looks like this:

C#
namespace MyApplication
{
    public class ContextContainerMock : IContextContainer
    {
        private static readonly IMyEntities context = new MyEntitiesMock();
        
        public IMyEntities Curren
        {
            get { return context; }
        }
    }
}

Now we have all the basics, let's move on to see how we use them.

Service and Entity Class

In my design, I have a service and data layer. The former consists of all the business logic while the latter is responsible solely for data access. This design allows me to have decoupled layers which can be easily scaled and maintained. This is what the CustomerService class looks like:

C#
using System.Data.Objects;
 
namespace MyApplication
{
    class CustomerService
    {
        protected readonly IContextContainer container;
 
        public CustomerService(IContextContainer container)
        {
            this.container = container;
        }
 
        public Customer GetCustomer(int id)
        {
            // you may have some business logic here
            var customer = new Customer(this.container);
            return customer.Select(id);
        }
    }
}

As you can see, the GetCustomer method could perform some business logic, then call the Select() method from the entity class, which looks like:

C#
public partial class Customer
{
    private readonly IContextContainer container;
 
    public Customer()
    {
    }
 
    public Customer(IContextContainer container)
    {
        this.container = container;
    }
 
    public Customer Select(int id)
    {
        return this.container.Current.Customers.FirstOrDefault(x =>x.ID == id);
    }
}

Note that it's declared as a partial class since we have another partial class of Customer from the generated code. The Select() method here is simply EF magic; i.e., going to the data source, getting the data then mapping it to a Customer object to return.

Now we have everything in our project, let's see how we're going to unit test the GetCustomer() method in our service class.

Unit Test

In our unit test code, all we have to do is to use the ContextContainerMock to return a mock entity class that implements IMyEntities. Then, we can call/test those methods as we wish:

C#
[TestMethod]
public void GetCustomer()
{
    ContextContainerMock container = new ContextContainerMock();
    IMyEntities en = container.Current;
 
    Customer c = new Customer { ID = 1, FirstName = "John", LastName = "Doe" };
    en.Customers.AddObject(c);
 
    CustomerService service = new CustomerService(container);
    var a = service.GetCustomer(1);
 
    Assert.AreEqual(c.FirstName, a.FirstName);
    Assert.AreEqual(c.LastName, a.LastName);
}

What About Methods that Call Stored Procedures?

If your methods use stored procedures and function import in the EF model, you can still mock test them by applying one more change to the generated code.

Suppose you have a stored procedure called GetCustomers which simply returns all customer rows in the Customer table. Once you include the stored procedure in your EF model, complete the function import part and save the model you should have generated code like this:

C#
public ObjectResult<Customer> GetCustomers()
{
    return base.ExecuteFunction<Customer>("GetCustomers");
}

You'll need to change the returned type from ObjectResult<T> to IEnumerable<T> so you can declare this method in the IMyEntities interface:

C#
public interface IMyEntities
{
    IObjectSet<Customer> Customers { get; }
    IObjectSet<Order> Orders { get; }
    IEnumerable<Customer> GetCustomers();
    int SaveChanges();
}

Then you can add the code in MyEntitiesMock when this method is called from your mock test. Basically, it just returns a new instance of MockObjectSet<GetCustomersResult>:

C#
public IEnumerable<GetCustomersResult> GetCustomers
{
    get { return customers  ?? (customers = new MockObjectSet<GetCustomersResult>()); }
}

Finally, in your mock unit test, you'll do something like:

C#
[TestMethod]
public void GetCustomers()
{
    ContextContainerMock container = new ContextContainerMock();
    IMyEntities en = container.Current;
 
    GetCustomersResult c1 = new GetCustomersResult 
    { ID = 1, FirstName = "John", LastName = "Doe" };
    GetCustomersResult c2 = new GetCustomersResult 
    { ID = 2, FirstName = "Mary", LastName = "Doe" };
 
    en.GetCustomersResult.AddObject(c1);
    en.GetCustomersResult.AddObject(c2);
 
    CustomerService service = new CustomerService(container);
    var a = service.GetCustomers();
 
    Assert.AreEqual(c1.FirstName, a[0].FirstName);
    Assert.AreEqual(c2.FirstName, a[1].FirstName);
}

Summary

In this article, we see how we could mock test an EF Model-First project with some minor changes to the generated code from the ADO.NET Entity Data Model template. I think it's a small price to pay to be able to do your mock tests. More importantly, it allows you to easily automate your unit tests with a CI build process without worrying about having and maintaining a DB instance just for those tests. After all, the purpose of your unit testing should be validating your logic, not data.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Linus has more than 10 years of experience in designing and implementing enterprise scale applications. He is a seasoned architect in both J2EE and Microsoft technologies. He is also a Microsoft Certified Solution Developer for .NET.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Stephen Inglish29-Aug-12 9:32
Stephen Inglish29-Aug-12 9:32 
GeneralRe: My vote of 5 Pin
linush29-Aug-12 10:30
linush29-Aug-12 10:30 
QuestionRe: My vote of 5 Pin
Stephen Inglish29-Aug-12 11:33
Stephen Inglish29-Aug-12 11:33 
AnswerRe: My vote of 5 Pin
linush4-Sep-12 11:21
linush4-Sep-12 11:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.