Walkthrough: Test-Driven Development with the Entity Framework 4.0
This walkthrough will demonstrate how to use the new Plain Old CLR Object (POCO) support in Entity Framework 4.0 to develop an application using Test-Driven Development (TDD).
In the first release of EF, entity classes had to inherit from EntityObject or implement one of the EF interfaces, which meant your classes had to have direct dependencies on the EF. As a result, writing unit tests that verified the logic for just your domain objects was challenging. The tests would have to interact with the database directly, which violates the principle of persistence ignorance, and results in the tests running orders of magnitude slower.
With Entity Framework 4.0, you can use the Repository and Unit of Work pattern to create domain entities that are completely EF-agnostic. With the hard dependency on the EF removed, it’s much easier to create fakes and mocks of these objects when writing unit tests. These tests no longer need to hit the database at all, offering a clean separation of concerns, reducing complexity, and making unit tests run significantly faster.
For more background information on TDD improvements in EF 4.0, check out Diego’s excellent post here.
Requirements
1. This walkthrough requires Visual Studio 2010 Beta 2.
2. Download and extract the initial solution attached to this post.
3. If you want to run the application against an actual database, you can generate it from the model. However, the whole point of the walkthrough is to allow not working against an actual database, but rather to use in-memory fake objects instead.
Walkthrough
In this walkthrough, we’re going to use the repository pattern to encapsulate our database interaction, and create fake objects that allow us to test our domain logic without having to touch the database.
Part 1 – Getting Familiar with the Project
1. Open the attached solution and double-click on Blogging.edmx. We’ll be using the same model we used in the CodeOnly walkthrough:
2. In the designer click on the white background surface and press F4 to bring up the properties window. You’ll see that the “Code Generation Strategy” property has already been set to “None”. This tells the designer not to generate code for each entity. Instead we’ll be writing our own POCO entity classes. The Entity Framework’s POCO support is the critical piece needed to be able to write domain objects that can be tested easily.
3. Open each of the Entity classes (i.e. Blog.cs, Person.cs etc). Notice how they’re just regular POCOs; they don’t inherit from EntityObject or any other class/interface in the Entity Framework.
4. Open the file BloggingEntities.cs. This class inherits from ObjectContext and exposes an ObjectSet<TEntity> for each entity in the model:
public partial class BloggingEntities : ObjectContext
{
public virtual ObjectSet<Blog> Blogs { ... }
public virtual ObjectSet<Comment> Comments { ... }
public virtual ObjectSet<Person> People { ... }
public virtual ObjectSet<Post> Posts { ... }
public virtual ObjectSet<User> Users { ... }
...
}
Unlike our POCO objects, this class does have direct dependencies on types defined in the Entity Framework. If we write our tests to use this class then our tests would be hitting the database. Since we want to avoid this, we’ll spend the rest of the walkthrough exploring how to make a fake implementation of this class that our testbed can consume.
Part 2 – Setting up the Tests
5. In Visual Studio, click Test -> “New Test…”, select “Basic Unit Test”, and call the file “CommentTests.cs”. Click OK, then type “BlogTests” when prompted to name the project and click “Create”. Visual Studio will create a new Test Project where we can write our unit tests.
6. Add a reference from the Test project to the BlogManager class library.
7. Save the solution.
Part 3 – Implementing the Repository Pattern
8. Add a new interface to BlogManager and call it IBloggingEntities.cs. Paste in the following code:
using System;
using System.Data.Objects;
namespace BlogManager
{
public interface IBloggingEntities : IDisposable
{
IObjectSet<Blog> Blogs { get; }
IObjectSet<Comment> Comments { get; }
IObjectSet<Person> People { get; }
IObjectSet<Post> Posts { get; }
IObjectSet<User> Users { get; }
int SaveChanges();
}
}
This interface represents all the important operations on our ObjectContext (which is in BloggingEntities.cs). Now our tests can work against the interface rather than against the concrete object type, which will make it easier for us to substitute a fake ObjectContext. Substituting a fake means we can remove the need to interact with the database, which will speed up the test and eliminate the hassle of needing to reset the database to its initial state after each test run.
Similarly, each of the properties in the interface return IObjectSet instead of ObjectSet. Since we’re returning the interface type, we’ll also be able to substitute our own FakeObjectSet implementations for testing.
9. Modify the BloggingEntities class so that it implements the IBloggingEntities interface.
When you select the option to implement the interface from the smart tag, the IDE will generate the necessary properties at the bottom of the class. Replace the NotImplementedExceptions with the following code:
IObjectSet<Blog> IBloggingEntities.Blogs
{
get { return this.Blogs; }
}
IObjectSet<Comment> IBloggingEntities.Comments
{
get { return this.Comments; }
}
IObjectSet<Person> IBloggingEntities.People
{
get { return this.People; }
}
IObjectSet<Post> IBloggingEntities.Posts
{
get { return this.Posts; }
}
IObjectSet<User> IBloggingEntities.Users
{
get { return this.Users; }
}
10. Add a new class to BlogManager called BlogRepository.cs. Paste in the following code:
using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
namespace BlogManager
{
public class BlogRepository
{
private IBloggingEntities _context;
public BlogRepository(IBloggingEntities context)
{
if (context == null)
throw new ArgumentNullException("context was null");
_context = context;
}
}
}
The repository will act as a thin mapper between our domain objects (Blog, Post, User etc) and our Data Access Layer (our BloggingEntities class which inherits from ObjectContext). The key is that the repository itself is persistence ignorant, it’s just going to work against the IBloggingEntities interface, and doesn’t care what the actual backing store is.
As we go on and write our tests, we’ll let the tests dictate which methods we actually need to create in our repository.
Part 4 – Writing the Tests
11. In order to test out the Comment class we’re going to need some sample data since it has relationships to other entities. Replace the code in the CommentTests class with the following:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BlogManager;
namespace BlogTests
{
[TestClass]
public class CommentTests
{
private IBloggingEntities _context;
[TestInitialize]
public void TestSetup()
{
Person p = new Person()
{
fn = "Jonathan",
ln = "Aneja",
email = "jonaneja@microsoft.com",
User = new User() { Password = "password123" }
};
Blog b = new Blog()
{
Name = "Entity Framework Team Blog",
Url = "https://blogs.msdn.com/adonet",
User = p.User
};
Post post = new Post()
{
ID=1,
Blog = b,
Created = DateTime.Now,
Posted = DateTime.Now,
Title = "Walkthrough: Test-Driven Development in Entity Framework 4.0",
User = p.User,
User1 = p.User,
PermaUrl = b.Url + "/walkthrough-TDD-in-Entity-Framework-4.0.aspx",
Body = "This walkthrough will demonstrate how to..."
};
}
}
}
12. Before the end of the TestSetup method type in the following code, then press Ctrl+Dot to bring up the following smart tag:
Press Enter and the IDE will automatically create the FakeBloggingEntities class. (Note there’ll still be a compile error since the auto-generated class doesn’t implement IBloggingEntities yet. We’ll add that in a few steps).
13. Type the following code at the bottom of TestSetup():
For each method use the Ctrl+Dot plus Enter trick to generate the actual methods. What’s great about this feature is it allows you to keep your flow without having to switch between windows constantly.
14. Now we’re ready to write our first test. The functionality we want to verify is that our application will reject any comment that’s been double-posted to the same entry. Paste in the following method into the CommentTests class:
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void AttemptedDoublePostedComment()
{
var repository = new BlogRepository(_context);
Comment c = new Comment()
{
ID = 45,
Created = DateTime.Now,
Posted = DateTime.Now,
Body = "EF4 is cool!",
Title = "comment #1",
Person = repository.GetPersonByEmail("jonaneja@microsoft.com"),
Post = repository.GetPostByID(1),
};
Comment c2 = new Comment()
{
ID = 46,
Created = DateTime.Now,
Posted = DateTime.Now,
Body = "EF4 is cool!",
Title = "comment #1",
Person = repository.GetPersonByEmail("jonaneja@microsoft.com"),
Post = repository.GetPostByID(1),
};
repository.AddComment(c);
repository.AddComment(c2);
repository.SaveChanges();
}
Again, use the Ctrl+Dot plus Enter trick to generate method stubs for GetPersonByEmail, GetPostByID, and AddComment.
Notice that the Body, Title, and Person properties are all the same for both comments (we’ll implement logic in the Comment class later to throw an exception when attempting to double-post a comment).
Also notice the ExpectedException attribute at the top of the method. This tells the test infrastructure to expect an InvalidOperationException for this method, and fail if such an exception is not thrown.
15. Add a reference to System.Data.Entity.dll (this is required so that we can use the IObjectSet<T> interface in our fake classes).
16. In FakeBloggingEntities, add the using statements below, implement the IBloggingEntities interface, and press Ctrl + Dot plus Enter to automatically generate the methods.
Do the same thing for the IDisposable interface as well.
17. Click Test -> Run -> All Tests in Solution. We want to ensure the test fails since we haven’t actually implemented any logic yet.
18. Now we’ll start writing the code to make our test pass. Remove all the NotImplementedExceptions from FakeBloggingEntities and for each IObjectSet property implement the following pattern (i.e. do it for User/Post/Person/Comment as well) :
public IObjectSet<Blog> Blogs
{
get
{
return _blogs ?? (_blogs = new FakeObjectSet<Blog>());
}
set
{
_blogs = value as FakeObjectSet<Blog>;
}
}
private FakeObjectSet<Blog> _blogs;
Also implement SaveChanges and Dispose:
public int SaveChanges()
{
foreach (var comment in Comments)
{
((IValidate)comment).Validate(ChangeAction.Insert);
}
return 1;
}
public void Dispose() {}
19. Create the following FakeObjectSet<T> class. This will be a fake implementation of an IObjectSet that is backed by an in-memory HashSet<T> (rather than by a database table). IObjectSet derives from IQueryable and so we need to implement its members as well:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Objects;
namespace BlogManager
{
public class FakeObjectSet<T> : IObjectSet<T> where T : class
{
HashSet<T> _data;
IQueryable _query;
public FakeObjectSet() : this(new List<T>()) { }
public FakeObjectSet(IEnumerable<T> initialData)
{
_data = new HashSet<T>(initialData);
_query = _data.AsQueryable();
}
public void Add(T item)
{
_data.Add(item);
}
public void AddObject(T item)
{
_data.Add(item);
}
public void Remove(T item)
{
_data.Remove(item);
}
public void DeleteObject(T item)
{
_data.Remove(item);
}
public void Attach(T item)
{
_data.Add(item);
}
public void Detach(T item)
{
_data.Remove(item);
}
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();
}
}
}
20. Remove all the NotImplementedExceptions from BlogRepository and implement the following logic:
public void AddPerson(Person p)
{
_context.People.AddObject(p);
}
public void AddBlog(Blog b)
{
_context.Blogs.AddObject(b);
}
public void AddPost(Post post)
{
_context.Posts.AddObject(post);
}
public void SaveChanges()
{
_context.SaveChanges();
}
public Person GetPersonByEmail(string email)
{
return _context.People.First(p => p.email == email);
}
public Post GetPostByID(int postID)
{
return _context.Posts.First(p => p.ID == postID);
}
public void AddComment(Comment c)
{
_context.Comments.AddObject(c);
}
21. At this point run the tests again. Now that we’ve written all the infrastructure, the entire test can actually run, and we get the following result:
22. Replace the existing IValidate.Validate method in the Comment class and add a using directive for System.Linq.
void IValidate.Validate(ChangeAction action)
{
if (action == ChangeAction.Insert)
{
//prevent double-posting of Comments
if (this.Post.Comments.Count(c => c.Body == this.Body &&
c.Person.User == this.Person.User) > 1)
throw new InvalidOperationException(
"A comment with this exact text has already been posted to this entry");
}
}
23. Now run the test again, and this time it passes!
24. We’re going to add one more feature – we want to validate that any comment posted to the blog actually contains some text. Now that we have all of our infrastructure wired up, adding the feature and its corresponding test can be done very quickly.
First we’ll write the test that verifies this functionality and add it to the CommentTests class:
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void AttemptBlankComment()
{
var repository = new BlogRepository(_context);
Comment c = new Comment()
{
ID = 123,
Created = DateTime.Now,
Posted = DateTime.Now,
Body = "",
Title = "some thoughts",
Person = repository.GetPersonByEmail("jonaneja@microsoft.com"),
Post = repository.GetPostByID(1),
};
repository.AddComment(c);
repository.SaveChanges();
}
Go ahead and run the test and verify that it fails:
25. In the Comment class’ Validate method add the following code:
if (String.IsNullOrEmpty(this.Body))
throw new InvalidOperationException(
"Comment must contain some text in its Body");
26. Run both tests and this time they should both pass:
Summary
In this walkthrough we explored how Entity Framework 4.0’s POCO support makes it easier to create a testable, persistence-ignorant architecture. We saw how to implement the Repository pattern, and take advantage of new IDE features that make TDD easier. Finally, we saw how IObjectSet<T> makes it easy for us to create fake implementations that can be tested without having to touch the database.
We’d love to hear your feedback, so let us know what you think!
Jonathan Aneja
Program Manager, Entity Framework Team
Comments
Anonymous
December 17, 2009
How would you go about testing GetPersonByEmail()? Would you use an expression visitor and parse the expression?Anonymous
December 17, 2009
Interesting...but what about the poco template ? It's missing in ctp 2. Should I have to wait for it until the rtm ?Anonymous
December 18, 2009
I've been doing much the same thing in EF v1. This pattern works fine with or without POCOs. The biggest issue I have is something which isn't addressed here, and which is common to unit testing with just about any LINQ provider: LINQ to Entities has different rules for LINQ to Objects. Some of these you can work around, and the improved LINQ support in v4 is going to help a lot. But the one that really gets me is null coalescing. For example, in an ASP.NET MVC controller action I can write: var q = from f in Repository.SelectAllFoos() where f.SomeNullableRelationship.Id == id select f; And this works fine in LINQ to Entities, since it coalesces nulls. But LINQ to Objects will die on this statement if SomeNullableRelationship is in fact null, since it does not coalesce nulls, and will try and be reference Id. This is a trivial example, but the same issue pops up over and over again. I'm left with the choice of writing less-than-ideal LINQ to Entities with null checks included or not testing the portions of the code which would be exercised when the nullable relationship is in fact null.Anonymous
December 19, 2009
@Craig: Yes, there are a number of differences between LINQ to Objects and LINQ to Entities that are hard to abstract away, common examples being this kind of null lifting that happens in LINQ to Entities and the differences in null equality semantics. From your description, it sounds like you prefer the LINQ to Entities behavior in this case because it allows simpler queries without null checks. Is that right? It would be interesting for me to know what you think a reasonable solution would be. For instance, would you like to see an in-memory LINQ implementation that mimics relational database behavior? I would also like to hear what other differences you usually hit following this practice. Thanks, Diego.Anonymous
December 19, 2009
@Luca: we are working on an updated version of the POCO template for Beta 2. We still don't have a date, but you will see the announcement in this blog.Anonymous
December 19, 2009
I think it is great that the framework is being designed with testability in mind. However, I fear a bit of confusion between what testability means (testing) and what TDD is. See my blog post for more: http://geekswithblogs.net/wesm/archive/2009/12/19/that-is-testability-not-test-driven-development.aspx I also used this post as a spring board to discuss some testing tips that I have been gathering. http://geekswithblogs.net/wesm/archive/2009/12/19/testing-tips-that-is-testability-not-tdd-part-2.aspxAnonymous
December 20, 2009
The comment has been removedAnonymous
December 22, 2009
I agree with the comment from Wes. This isn't TDD.Anonymous
January 03, 2010
Interesting approach. I've been creating a simple FakeContext class which contains ILists<Products>, IList<Categories>, etc.. for all my inmemory 'stubbed' data which i use for testing, etc. So i tried using a FakeObjectSet<T> class instead. My biggest problems i found where :-
- Auto incrementing any Identies.
- (Lack of) Uncommited data. Because i'm using the Unit Of Work pattern, I have a method on the Context (be it the FakeContext .. in this example, class BloggingEntities ro FakeBloggingEntities) that calls the SaveChanges. eg. #region IUnitOfWork Members public void Commit() { SaveChanges(); } #endregion Now, this works great for the real context, because it inherits the class ObjectContext. This doesn't work for the FakeBloggingEntities class, because it cannot inherit from ObjectContext. So then i thought, maybe i need two FakeObjectSet<T> instances, per entity. So, with your example.. maybe.. private FakeObjectSet<Blog> _blogs; private FakeObjectSet<Blog> _uncommitedBlogs; and all AddObjects go to the uncommited instance .. and the SaveChanges (or Commit if doing a UoW) could then move them over from uncommited to commited ... and increment the identity. but this is what i already have with my simple FakeContext class that contains IList<T>'s instead of FakeObjectSets ... so why would I want to use a FakeObjectSet<T> ?
Anonymous
January 20, 2010
What about ESql mocking? Use Linq to EF is easy because L2E is written in the same manner as L2O and it's ignorant to the way it's executed, but with ESql you HAVE to use ObjectQuery<T>. How would you solve this in TDD?Anonymous
March 01, 2010
> The key is that the repository itself is persistence ignorant, > it’s just going to work against the IBloggingEntities interface, and doesn’t care what the actual backing store is. Hmm, if the repository would be persistence ignorant, then you should be able to replace tbe Entity Framework implementation with NHibernate as the backing store, right ? So, if the programmer some day would want to use NHibernate as implementation instead, then we would make an NHibernate class that implements IBloggingEntities, to use it as a parameter to the BlogRepository constructor. The the NHibernate class implementation would (through its interface) use System.Data.Objects.IObjectSet (and require a reference to System.Data.Entity.dll ) as return type in most of its signatures. Is that really persistance ignorant ? I mean, is the interface IObjectSet supposed to be a part of a generic API that different implementations should use ? Previously I have mainly been a Java developer and am new to the Entity Framework, but can it maybe be compared with the Java Persistence API (JPA) which defines a persistence API that different OR mappers can implemenent, for example Hibernate. Is the Entiry Framework a similar API which different OR mappers, e.g. NHibernate should implement ? In that case I really must have understood something, since I have believed that the Entity Framework is a Microsoft only API + Microsoft only implementation, but otherwise, I do not think it makes sense to claim that IBloggingEntities is persistence ignorant. / TomasAnonymous
March 11, 2010
I have a code generator that creates a replacement for all the EF classes, and implements a (somewhat limited) in memory context that allows me to test fast in the debug configuration without touching the EdmGen generated code. It even supports relationship updates on Property assignment or ObjectSet addition (something that your example appears not to support). I still test in my CI server using the real database, and there are corner cases that depend on trigger's or sprocs that cannot be tested using my fake. What I would really like, is an in memory database that understood eSQL, that way the tests would only depend on a .config change to run fast. Barring that, the next thing would be to implement an in memory replacement for the behavior seen in the ObjectContext that followed the EdmGen'd code. Regards, PabloAnonymous
March 13, 2010
The examples overlook the hard part of the problem. Testing gets hard when dependencies can't be faked. All of the stuff above is our own classes, or interfaces that we can create fake instances for (like IObjectSet<TEntity>). The problem with testing here is not with these classes, it's with ObjectContext itself. It is not based on an interface and has no virtual methods. The only way to fake it is to encapsulate it. That's just painful and ugly (and not shown in the examples). The examples show a fake SaveChanges() method, but they don't show how to test your actual SaveChanges() method. That (and any other use of ObjectContext) requires faking ObjectContext.Anonymous
April 16, 2010
The comment has been removedAnonymous
April 16, 2010
Looks like EF is still a fail! I had hopes for you guys this round but it looks like you don't really understand the issues or the patterns used.Anonymous
May 27, 2010
Why are all ObjectSets marked as virtual?Anonymous
September 04, 2010
What about relationships between IObjectSets? Is there a way to fake the navigation properties?Anonymous
September 09, 2010
I'm confused on how this approach would work with EF4 and the POCO T4 templates? DavidAnonymous
March 24, 2011
Thanks for the code and explanations. I used this successfully unitl I ran into the following situation: You know how you create a new User while creating a new Person instance in TestSetup? That does not append the User object in the "Users" objectset. I tested this by adding a method to BlogRepository, to return all Users. It returned 0 even though the code had created the User object for the Person object. What would be the right approach to work around this? Would it be worthwhile iterating over all navigation properties and adding them to their objectsets?Anonymous
July 29, 2012
I just wrote a comprehensive response and the website lost my work! Argh!Anonymous
July 29, 2012
Johathan, Please accept my apology, but this fake pattern is way too much work. I tried following your walkthrough with the variation that I am using DbContext. When I was coding for each entity operation I realized why I do not like the pattern. It is too verbose. After leveraging a code generator to create DbSets which inherit operations why would I want to code all that over again manually? And imagine an agile project team, changing the schema frequently, and breaking the elaborate fake frequently. Entities get renamed, columns are added and removed, associations are changed, etc. And I will need a Task just to fix the elaborate fake before I can start using the new schema for the real project work. It just feels all wrong. I prefer mock over fake and this walkthrough reminded me why. Thanks for that. blogs.msdn.com/.../productivity-improvements-for-the-entity-framework.aspx I read that the EF team included interfaces in the DbContext release to help us mock. I found the IDbSet and got excited. I created my own interface which includes both my T4 generated DbSets as IDbSets AND all the public features of the DbContext. I started coding my mocks, using Moq, and the code was building. I was dreaming of changing the tt file to help me. But when I started running the tests they failed because the DbContext is not initialized. The DbSets are null and cannot accept the .Add. I think I might be close to getting this mocking approach to work. Has anyone implemented mocking with DbContext? I would be really glad to see a post on that. Sincerely, JoeAnonymous
July 30, 2012
@Joe Kahl – I’ve replied to these same questions on some of the other posts that you commented on. If you are still running into issues then please start up a http://stackoverflow.com/ thread and tag it with ‘entity-framework’ and ‘moq’.Anonymous
October 24, 2013
Hi, I need your inputs on writing unit test cases for entity framework 4. We have build a enterprise application and it's in final stage. Now we added a module which fully uses work flow with entity framework for all database activity. so I need to mock all database calls. As per your article I need to define an interface with a method for all the tables, need to change in object context instance creation statements. Since the project is in final stage, I don't want to do change in at Entities class level. Is there any other way where I can mock the database calls. Thanks, Prakash. ps_prakash02@yahoo.co.inAnonymous
October 28, 2013
@Prakash. - The techniques shown in this post don't require you to change the entity classes themselves, just the context class (to swap to IObjectSet and implement the interface you define for your context). If you are after info about testing with later versions of EF then this post will help you with EF4.1 thru EF5 - romiller.com/.../testing-with-a-fake-dbcontext. We made some improvements in EF6 to make testing easier too - msdn.microsoft.com/.../dn314429.Anonymous
April 24, 2014
Alternatively, you can download my project on Codeplex. I have a working example that you can copy. entityinterfacegenerator.codeplex.com It generates the interface files that you need for IoC and unit test purposes.