Domain Driven Design & Test Driven Development\Design with Entity Framework, Part 1.a Refactoring & Unit Testing

by mosessaur| 11 February 2009| 6 Comments

In I talked about building domain model for Northwind using Entity Framework. I used the generated ObjectContext class which is NorthwindDataContext as the basic Data Access Layer Helper. But my NorthwindDataContext is implementing custom interface I created in order to be able to make my Data Access Layer testable as well as to be independent from EF.

My target is to achieve decoupling between EF and all layers above it. This include Repository classes. This might not be of a big benefit to everyone, because my repositories implementation will still dependent on IDataContext. But for me it will help to do TDD more smoothly regardless of my underlying data access layer. Beside it might allow me in future to be able to switch from EF to LINQ to SQL and just implement my IDataContext interface for LINQ to SQL.

Source code is attached to this post, feel free to download it and explore the code

Refactoring of IDataContext and Models:

After some discussion with Davy Landman and getting some feedback from him. I made some refactoring to IDataContext although me an him have different ideas few conflicts. So basically here is what I need from IDataContext to support:

  • Being able to retrieve all records of Categories, Products & Supplier
  • Being able to filter these entities by unique identifier to return single record.
  • Being able to register new record for insertion or deletion.
  • Being able to persist changes to the underlying data source.

The above list is not the only features I need, in future it must grow, like being able to filter Products by category and so on.

I hear some of  you saying, What! this is the job of repository classes to build queries and perform data access. and I will answer yes and I totally agree, but I provide IDataContext as a huge repository of queries that is actually wrapped by Repositories. I think this concept is applicable with some ORM tools such as EF and LINQ to SQL. But might not work for other things such as classical Data Access Layers and NHibernate. So simply I consider IDataContext part of repository classes.

The implementation of IDataContext which is NorthwindDataContext class includes some helper methods & properties to assist me to work with EF and being able to test it. But generally I am hiding most the features provided by EF generated code with these methods & properties. Because simply that generated code is not testable and you cannot use to set expectations when you start to use mocking.

Below is the class diagram of my refactored model with data access layer helper:

NorthwindClassDiagram

Also I zoomed on NorthwindDataContext class and provided the following diagram with comments about 2 helper methods:

NorthwindDataContextClassDiagram

Here is the code of GetEnumrable & GetFilteredDataSource methods with explanation of each method's role :

/// <summary>
/// Used as helper method to provide casting from EDM primative data type 
/// (generated model entity) such Category to model Interface such ICategory 
/// which is implemented by the generated EDM entity class.
/// </summary>
/// <typeparam name="I">Type of the model interface</typeparam>
/// <typeparam name="E">
/// Type of the EF primative data type (generated model entity)
/// "E" (model entity) must implement "I" (model interface)
/// </typeparam>
/// <param name="datasource">Data source to be filtered</param>
/// <param name="filter">Filter expression used to filter datasource</param>
/// <returns>IEnumrable of I (model interface)</returns>
private static IEnumerable<I> GetEnumerable<I, E>(IQueryable<E> datasource, Expression<Func<E, bool>> filter)
 where I : class, IEntity
 where E : class, I
{
 var filteredQry = (filter != null) ? datasource.Where(filter) : datasource;
 foreach (var item in filteredQry)
     yield return item as I;
}

/// <summary>
/// Takes as a parameter Linq filter Expression to be used to filter data source set
/// such as CategorySet. 
/// This will help to provide filters that will perform filtering on underlying 
/// data source side and not in memory filtering using LINQ to Objects.
/// This method calls GetEnumerable<I, E> providing it with the correct data source set.
/// </summary>
/// <typeparam name="I">Type of the model interface</typeparam>
/// <typeparam name="E">
/// Type of the EF primative data type (generated model entity)
/// "E" (model entity) must implement "I" (model interface)
/// </typeparam>
/// <param name="datasource">Data source to be filtered</param>
/// <param name="filter">Filter expression used to filter datasource</param>
/// <returns>IQueryable of I (model interface)</returns>
private IQueryable<I> GetFilteredDataSource<I, E>(Expression<Func<E, bool>> filter)
 where I : class, IEntity
 where E : class, I
{
 IQueryable<E> queryable = null;

 if (typeof(I) == typeof(ICategory))
     queryable = this.CategoryTable as IQueryable<E>;
 else if (typeof(I) == typeof(IProduct))
     queryable = this.ProductTable as IQueryable<E>;
 else if (typeof(I) == typeof(ISupplier))
     queryable = this.SupplierTable as IQueryable<E>;

 return GetEnumerable<I, E>(queryable, filter).AsQueryable() as IQueryable<I>;
}

Now if I wanted to filter Category set data source by Category Id I shall use GetFilteredDataSource by a way or another. That is why I am using in FindXxxById methods as the following:
public ICategory FindCategoryById(int id)
{
 return this.GetFilteredDataSource<ICategory, Category>(c => c.Id == id).FirstOrDefault();
}

public IProduct FindProductById(int id)
{
 return this.GetFilteredDataSource<IProduct, Product>(c => c.Id == id).FirstOrDefault();
}

public ISupplier FindSupplierById(int id)
{
 return this.GetFilteredDataSource<ISupplier, Supplier>(c => c.Id == id).FirstOrDefault();
}

Coding Testable EF ObjectContext Class:

Before starting this point, let me clarify what does Testable means. Testable means being able to write Unit Test code that can test your coded independent of the environment, database or anything similar. And being able to mock your classes. Simply being able to write Unit Tests that follow the guide lines mentioned in my .

That was my target for NorthwindDataContext class, is being able to test it independent of the database, and confirm the success of my test by doing integration test that also success.

Someone would say and why I want to test something that is already tested (by Microsoft)? Well because later you'll need to test classes that depends on this class. If you used the auto generated EF code as it is you'll not be able to set expectations out of it to be able to test independent of the database.

The auto generated code is not testable, because it requires to hit the database and you cannot mock it. In order to be able to stay way of database you need to have an alternative. The common alternative is in memory storage using Lists.

Using generated EntitySet types such as CategorySet properties will require database. To workaround this I created few helper public virtual properties that wrap auto generated EntitySets such as CategorySet as the following:

/// <summary>
/// Return EF Coupled IQueryable of Category. Used for testing purpose only
/// </summary>
public virtual IQueryable<Category> CategoryTable
{
 get{ return this.CategorySet; }
}
/// <summary>
/// Return EF Coupled IQueryable of Product. Used for testing purpose only
/// </summary>
public virtual IQueryable<Product> ProductTable
{
 get{ return this.ProductSet; }
}
/// <summary>
/// Return EF Coupled IQueryable of Supplier. Used for testing purpose only
/// </summary>
public virtual IQueryable<Supplier> SupplierTable
{
 get{ return this.SupplierSet; }
}

Now why these helper properties are virtual public. Public because I want to be able to access them throw my unit test classes. Virtual because Moq will require these properties to be virtual in order to be able to override them to return my specified expectations. Same concept is applied to some public methods such AddObject and DeleteObject methods. But because these properties or methods are not interface based; so they will not be accessible through repositories as I'll show on my upcoming posts.

The above properties are invoked through GetFilteredDataSource method.

Writing Unit Test for EF ObjectContext Class:

In TDD is supposed to write your tests first, fail them and start fix what is broken and make your code run. Then do refactoring over and over to formalize your code. My first test attempt will fail because of NotImplementedException thrown by IDataContext methods in NorthwindDataContext class. My test basically want to test the following:

  • Calling IDataContext (Categories,Products or Suppliers) properties should not return null.
  • Calling IDataContext FindXxxById methods should return correct Xxx entity.
  • Calling IDataContext Insert should call AddObject Method of NorthwindDataContext to insure that the object is being added to proper Entity Set and prepared for persistence.
  • Calling IDataContext Delete should call DeleteObject Method of NorthwindDataContext to insure that the object is being marked for deletion.

I am using Moq to assist me in my TDD. In this attempt I will need a mock instance of NorthwindDataContext to simulate its work independent of the underlying database. Also I am initializing few lists to act as in memory storage.

private readonly Mock<NorthwindDataContext> _context;
private readonly List<Category> _categories;
private readonly List<Product> _products;
private readonly List<Supplier> _suppliers;
public NorthwindDataContextTest()
{
    _context = new Mock<NorthwindDataContext>(ConfigurationManager
                                             .ConnectionStrings["NorthwindDataContext"]
                                             .ConnectionString);
    _categories = new List<Category>();
    _products = new List<Product>();
    _suppliers = new List<Supplier>();
}

On line 7 to 9 I am initializing new mock instance of NorthwindDataCotext. I am getting a connection string from application configuration file. you might ask why I would need a connection string. Actually this is a must thing in order to be able to load your EDM model. If you didn't provide this connection string the mocking will fail. The connection string in this test just specify the path of CSDL, SSDL and MSL. In my case they are embedded resources:

<configuration>
 <connectionStrings>
  <add name="NorthwindDataContext" 
       connectionString="metadata=res://*/Edm.Northwind.csdl|res://*/Edm.Northwind.ssdl|res://*/Edm.Northwind.msl;provider=System.Data.SqlClient;"
       providerName="System.Data.EntityClient" />
 </connectionStrings>
</configuration>

Writing Test Initialization with Visual Studio.Net Unit Testing:

As I am using VS.Net unit testing tools I am using the provided unit testing facilities. Such as Test Initialization feature. This is just a method marked with TestInitialize attribute as the following:

[TestInitialize]
public void TestInitialize()
{
    _context.ExpectGet(c => c.CategoryTable)
			.Returns(_categories.AsQueryable());
    
	_context.ExpectGet(c => c.ProductTable)
			.Returns(_products.AsQueryable());
    
	_context.ExpectGet(c => c.SupplierTable)
			.Returns(_suppliers.AsQueryable());
}

I am using the mocked NorthwindDataContext to set expectation to the helper properties I mentioned earlier. Again to be able to set expectations with Moq you'll need public virtual methods or properties. So Whenever a call is made to CategoryTable or ProductTable or SupplierTable the specified expectation for each one should be returned.

Now I want to test IDataContext properties (Categories, Products and Suppliers):

[TestMethod]
public void NorthwindDataContext_Categories_Returns_Category_Set()
{
    Assert.IsNotNull(_context.Object.Categories);
}
[TestMethod]
public void NorthwindDataContext_Products_Returns_Product_Set()
{
    Assert.IsNotNull(_context.Object.Products);
}
[TestMethod]
public void NorthwindDataContext_Suppliers_Returns_Supplier_Set()
{
    Assert.IsNotNull(_context.Object.Suppliers);
}

Hint: Unit test method in VS.Net are marked with TestMethod Attribute. Every time one of the test method being call TestInitialize Method will be called first as well as test class constructor.

I wish to trace the call of the above code. _context.Object will return a mocked instance of NorthwindDataContext. When calling Categories property this property invokes GetFilteredDataSource method which in turn calls CategoryTable helper property. When CategoryTable property is called it will return the expectation I specified in TestInitialize method. Same thing is applied for the other properties.

Time to test filtering methods such as FindCategoryById. I write test for such methods by preparing data to test with, then set my expectation. Finally perform tested method call and assert returned results, just as the following:

[TestMethod]
public void NorthwindDataContext_FindCategoryById_Returns_Correct_Category()
{
 //Prepare test data.
 for (int i = 1; i < 11;i++ )
     _categories.Add(Category.CreateCategory(i, String.Empty));

 //Get existing Id.
 int id = _categories[0].Id;

 //Set Expectation
 _context.ExpectGet(c => c.CategoryTable).Returns(_categories.AsQueryable());

 //Perform Test
 var category = _context.Object.FindCategoryById(id);

 //Assert
 Assert.IsNotNull(category);
 Assert.AreEqual(category.Id, id);
}

Note that I am testing single thing per unit test method. That doesn't mean you should perform single assert. In the above method I am testing one then but doing 2 asserts, one to ensure that the returned result is not null and the other assert to ensure that the returned result is the desired one.

My final test is to test Insert and Delete method. Actually in these tests I just want to check if AddObject or DeleteObject methods are being called. These helper methods uses internally the EF generated code to add object to the context or mark it for deletion. I don't want to test the underlying EF generated code, I just want to test mine.

[TestMethod]
public void NorthwindDataContext_Insert_Should_Call_AddObject()
{
 var category = Category.CreateCategory(1, String.Empty);

 _context.Expect(c => c.AddObject(category)).Verifiable();

 _context.Object.Insert(category);

 _context.Verify();
}

On line 6 I am setting my expectation as a call to AddObject method. And using Moq features I am asking to verify this call using Verifiable method of the mocked object. On line 10 I am calling Verify method of the mocked NorthwindDataContext. This method will verify that AddObject method is being called. If not the test will fail.

After running my test I got them all Green.That was a happy ending. This code should run on your machine and produce green results as well. You can download it and review the code and get me with some feedback and enhancements.

I would like to thank Davy Landman for his valuable ideas and thoughts and the references he provided me about DDD. I am new to DDD & TDD and I thought the best way to learn them is to share my thoughts with others, making mistakes and let somebody else correct me. So feel free to comment and provide me with more resources and ideas.

As this post already grow long I will keep my integration test for the next post. Where I will show that my assumptions and expectations during TDD unit testing applies also on my Integration testing.

Keep tuned.

Comments (6) -

Davy Landman
Davy Landman Netherlands on 2/9/2009 2:13 PM Thanks for mentioning me Smile

I think we have to agree do disagree on the fact that you try to push all the Architectural knowledge to your IDataContext. I still think that repositories are the place to put the architectural knowledge, here are a few resources discussing that same direction:

- http://martinfowler.com/eaaCatalog/repository.html
- www.tobinharris.com/.../ddd-repositories-in-the-wild
- http://arcware.net/nhibernaterepository/
- codebetter.com/.../ddd-the-generic-repository.aspx
- richarddingwall.name/.../
- http://www.searchfull.net/1819814.html
- evan.bottch.com/.../

I suspect the larger your application gets, the more problems you'll have with the IDataContract (and the larger it gets).

One example is paging, the Queryable returned by GetFilteredDataSource does not represent an Entity Framework Queryable, so the .Skip() and .Take() will be done in memory, so that will be a big performance problem when your tables begin to fill.

Another is the filtering of which data to fetch from the database, you might want to have just a subset, in EF you'd express this using the Select projection to express you're only interested in the Name of the categories
from c in Categories
select c.Name

The other way is for EF you'll might want to do some eager loading, so normally you'd use a
from p in Products.Include("Category")
select p

to fetch all the products and also do a join on the category table to not have to lazy load that property later.

Also I'm not that sure you should talk about tables anymore, Entity Framework calls them Set's because your abstracting away from your database, and one entity might map over 2 tables. I'd prefer to use the pural form to express your talking about a set of entities. But this is very subjective, and the Set post-fix of EF is also a good idea.

The FindCategoryById(int id) is not so much a find but more a Get/Fetch, so I'd rename that to GetCategory (ById is kinda overkill, the parameter tells you that allready).

I do like the way you've mocked the IDataContext to play nicely in memory Smile

Still curious to see how the repositories will look Wink
mosessaur
mosessaur Egypt on 2/10/2009 11:54 PM I agree with you Davy, I am giving too much responsibility to IDataContext and its implementation. Although I have answers to few of your concerns but still all will fill into giving too much resposnibility to IDataContext.

First time I thought this approuch was when I was trying to do everything as if I am not dealing with EF. I thought that my repositories shouldn't have any idea about the underlying data access infrastructure.
But it seems that I am wrong.

Anyway, I will refer to you suggested posts and see if I will do another round of refactoring Smile
Amr Elsehemy
Amr Elsehemy Egypt on 2/19/2009 4:15 AM Moses on fire.Smile
Nice work keep on writing excellent stuff.
mosessaur
mosessaur Egypt on 2/19/2009 3:24 PM @Amr Elsehemy thank you mate. I am sure you are aware of my updates. And I hope we both are on same track on bloggie
Martin
Martin United Kingdom on 7/22/2009 6:04 PM Have you seen the work done here?
blogs.msdn.com/.../default.aspx

In particular EF 4.0 with POCO and self tracking entities?

Thanks
Martin

mosessaur
mosessaur Egypt on 7/23/2009 7:47 AM Yes I did, I am following them step by step since the release of Beta 1.
This post was made before that and concerned with EF v1
Comments are closed