Repository and Unit of Work for Entity Framework (EF4) for Unit Testing

Tags: Entity Framework, c#, linq, EF4

If you are not familiar with the Unit of Work pattern you should firt look at Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application (9 of 10).  It's a great article that demonstrates what the Unit of Work pattern can do.

Unfortunately, that article uses a different version of Entity Framework.  I am using the default tools that come with Visual Studio and didn't want to modify the project too greatly so for me, the code wasn't usable.  I also wanted to expose the IQueryable interface which as a side effect also simplified my Repository design.

By using a Repository Pattern you'll find unit testing is now possible and quite easy to write.  This illustration (taken from asp.net) shows the differences between No Repository and the Repository Pattern.  Notice how there are no Unit Tests in the No Repository section also note the "Alternative Persistence Medium" node (this is where our MemoryRepository comes in).

The Unit of Work

The job of the Unit of Work class is to manage the lifecycle and context of your Repositories.  The class is pretty simple to create, implement IDisposable, add all your Repositories and give it a Save method.

This is what Northwind's Interface and Unit of Work class looks like:

(for the Repository code, check out my previous post: Generic IQueryable Repository for Entity Framework 4)

public interface INorthwindUnitOfWork : IDisposable
{
	IRepository<Category> CategoryRepository { get; }
	IRepository<Customer> CustomerRepository { get; }
	IRepository<Employee> EmployeeRepository { get; }
	IRepository<Order> OrderRepository { get; }
	IRepository<Order_Detail> Order_DetailRepository { get; }
	IRepository<Product> ProductRepository { get; }
	IRepository<Shipper> ShipperRepository { get; }
	IRepository<Supplier> SupplierRepository { get; }
        void Save();
}

public partial class NorthwindUnitOfWork : INorthwindUnitOfWork
{
	private readonly NorthwindDataContext _context;
	private readonly IRepository<Category> _categoryRepository;
	private readonly IRepository<Customer> _customerRepository;
	private readonly IRepository<Employee> _employeeRepository;
	private readonly IRepository<Order> _orderRepository;
	private readonly IRepository<Order_Detail> _order_DetailRepository;
	private readonly IRepository<Product> _productRepository;
	private readonly IRepository<Shipper> _shipperRepository;
	private readonly IRepository<Supplier> _supplierRepository;

	public IRepository<Category> CategoryRepository
	{
		get { return _categoryRepository; }
	}

	public IRepository<Customer> CustomerRepository
	{
		get { return _customerRepository; }
	}

	public IRepository<Employee> EmployeeRepository
	{
		get { return _employeeRepository; }
	}

	public IRepository<Order> OrderRepository
	{
		get { return _orderRepository; }
	}

	public IRepository<Order_Detail> Order_DetailRepository
	{
		get { return _order_DetailRepository; }
	}

	public IRepository<Product> ProductRepository
	{
		get { return _productRepository; }
	}

	public IRepository<Shipper> ShipperRepository
	{
		get { return _shipperRepository; }
	}

	public IRepository<Supplier> SupplierRepository
	{
		get { return _supplierRepository; }
	}

	public NorthwindUnitOfWork(NorthwindDataContext context)
	{
		_context = context;

		_categoryRepository = new EntityRepository<Category>(_context);
		_customerRepository = new EntityRepository<Customer>(_context);
		_employeeRepository = new EntityRepository<Employee>(_context);
		_orderRepository = new EntityRepository<Order>(_context);
		_order_DetailRepository = new EntityRepository<Order_Detail>(_context);
		_productRepository = new EntityRepository<Product>(_context);
		_shipperRepository = new EntityRepository<Shipper>(_context);
		_supplierRepository = new EntityRepository<Supplier>(_context);
	}

	public void Save()
	{
		_context.SaveChanges();
	}

	#region IDisposable Methods

	private bool disposed = false;

	protected virtual void Dispose(bool disposing)
	{
		if (!this.disposed)
		{
			if (disposing)
			{
				_context.Dispose();
			}
		}
		this.disposed = true;
	}

	public void Dispose()
	{
		Dispose(true);
		GC.SuppressFinalize(this);
	}

	#endregion
}

Unit Testing

(In this example I'm using Moq as my Mocking Framework)

I start by creating a method called GetMockCategoryRepository that creates a new MemoryRepository object and returns returns an IRepository<Category>.  This will mimic the behavior of our Category Repository with an in memory list, allowing us to test without hitting the database.

private IRepository<Category> GetMockCategoryRepository()
{
	var value = new MemoryRepository<Category>();
	value.Add(new Category { Category_ID = 1, Category_Name = "Beverages" });
	value.Add(new Category { Category_ID = 2, Category_Name = "Condiments" });
	value.Add(new Category { Category_ID = 3, Category_Name = "Confections" });
	return value;
}

Then in my test, I create a Mock INorthwindUnitOfWork, setup the CategoryRepository method to return my MemoryRepository and initialize a new NorthwindService object. For the Assert, we just need to check the count of items returned from the service is the same as our Repository!

[TestMethod]
public void NorthwindService_GetCategories_returns_category_names()
{
	// Arrange
	var mockUnitOfWork = new Mock<INorthwindUnitOfWork>();
	var mockCategories = GetMockCategoryRepository();

	mockUnitOfWork.SetupGet(x => x.CategoryRepository)
	              .Returns(mockCategories);

	var northwindService = new NorthwindService(mockUnitOfWork.Object);

	// Act
	var categories = northwindService.GetCategories();

	// Assert
	Assert.AreEqual(mockCategories.All().Count(), categories.Length);
}

Well, that's all you need.  You are now ready to full unit test your Services, Controllers or anything else that accesses your database.

Add a Comment