Sneak Preview: Entity Framework 4.0 Testability Improvements
The information in this post is out of date.
Visit msdn.com/data/ef for the latest information on current and past releases of EF.
Writing unit tests is a core practice in the vast majority of modern software development approaches. There are several benefits to it, but the one I personally tend to value the most is how I can take a component with enough unit test coverage and start refactoring it without worrying that I could accidentally introduce regressions.
With Test Driven Development and Unit Testing in general, it is critical that unit tests are lightning fast: in practice, you should run all tests for a component after applying each refactoring step, and that should not affect your rhythm. Whenever you feel the urge to skip the test runs, it is time to streamline your tests.
A well known way to accomplish this level of swiftness is to substitute the actual dependencies of the component being tested with test doubles. Test doubles expose the same API surface as the dependencies they impersonate, but instead of trying to emulate the often complex behavior of the real thing, they can limit themselves to provide canned responses or record the inputs received during the execution of the tests (which can help in verification even more).
When applied to the testing of the domain logic in an application using some O/RM technology, test doubles should ideally substitute the persistence layer. That way, unit tests typically never invoke the actual persistence framework, and more important, never in fact hit the database.
During the development Entity Framework 4.0, we spent a lot of time thinking how to make it easier to substitute Entity Framework in your unit tests. Here are a few improvements that we believe will make a difference on that front:
- POCO Support: As Faisal described in his post, you can now write your own entity classes that do not depend on Entity Framework, so you can instantiate them and use them more easily in your tests.
- Repository pattern: Repositories are the best known way to decouple the persistence layer from the rest of your application. While we won’t include or prescribe a particular variation of the pattern in the product, we have been working with Microsoft’s Patterns & Practices Team to produce new guidance and a Reference Implementation of this pattern (along with other patterns used Domain Driven Design) with Entity Framework. This exercise is helping us understand friction points and improvements we will hopefully address in this and future releases. Also, we will go deeper on this subject in a future blog post series.
- New IObjectSet<T> interface: the newObjectSet<T> class derives from ObjectQuery<T> and represents a “root” query object for an EntitySet, that also has AddObject, DeleteObject and Attach methods. In order to improve testability, we also defined the corresponding IObjectSet<T> interface that derives from IQueryable<T>. IObjectSet<T> happens to be super easy to implement as an in-memory fake object. Provided that your queries and CUD operations can refer to instances of IObjectSet<T>, your code will now be easier and faster to test.
- Template-based code generation: No matter what pattern variations you prefer for Repository, UnitOfWork, persistence contexts, or their interfaces and corresponding test doubles, if you think you might end up writing the same boilerplate code once and again, you are probably better off customizing one of our T4 templates to suit your testability needs.
- LINQ to Entities Improvements: Some popular approaches to testability involve redirecting LINQ queries that at runtime would be executed against a database to be evaluated in-memory (using LINQ to Objects) during the execution of unit tests. For those cases, the closer the behaviors of both LINQ implementations are, the lower the odds are that query bugs will go undetected until integration tests are executed. We have made several improvements in LINQ to Entities that we believe will help here:
a. Support for more LINQ operators, such as Contains, DefaultIfEmtpty, and improved support for Single/SingleOrDefault.
b. More translations for common Base Class Library patterns including various Math class methods, Guid.NewGuid, etc (some of this work is not included in Beta1).
c. Improved preservation of nested OrderBy operations.
d. Several bug fixes that further align the behavior of LINQ to Entities with LINQ to Objects.
The following sample code shows a very simple way to create and use a fake implementation of an ObjectContext that exposes properties of type IObjectSet<T>:
/// <summary>
/// Data access context interface for SimpleBlogging
/// </summary>
public interface
ISimpleBlogging
:
IDisposable
{
IObjectSet<Blog
> Blogs {
get
; }
IObjectSet<Post
> Posts {
get
; }
}
/// <summary>
/// Fake data access context for SimpleBlogging
/// </summary>
public class
SimpleBloggingFakeContext : ISimpleBlogging
{
private
FakeObjectSet<Blog
> _blogs;
public
IObjectSet<Blog
> Blogs
{
get
{ return _blogs ?? (_blogs = new
FakeObjectSet<Blog
>()); }
}
private
FakeObjectSet<Post
> _posts;
public
IObjectSet<Post
> Posts
{
get { return _posts ?? (_posts = new
FakeObjectSet<Post
>()); }
}
public void Dispose()
{
}
}
/// <summary>
/// Utility factory method for contexts
/// </summary>
private static
ISimpleBlogging
GetContext()
{
ISimpleBlogging
context = new
SimpleBloggingFakeContext
();
InitializeWithCannedData(context);
return context;
}
[
TestMethod
]
public void TestAddPost()
{
using (
ISimpleBlogging
context = GetContext())
{
var blogging = new BloggingService(context);
var newPost = blogging.AddPost(
"Diego",
"Hiatus",
"This is my last post for a while.");
var blog = context.Blogs.Single(b => b.ID == "Diego");
Assert
.AreSame(newPost.Blog, blog);
}
}
Stay tuned for a more in depth discussion of testability improvements, as well as other topics mentioned in this post.
Thanks,
Diego Vega
Program Manager, Entity Framework
Comments
Anonymous
May 18, 2009
PingBack from http://asp-net-hosting.simplynetdev.com/sneak-preview-entity-framework-40-testability-improvements/Anonymous
May 18, 2009
This looks terrific. Well done.Anonymous
May 18, 2009
Thank you for submitting this cool story - Trackback from progg.ruAnonymous
May 18, 2009
Thank you for submitting this cool story - Trackback from DotNetShoutoutAnonymous
May 18, 2009
Ooooh, please hit me with more of those! Good job guys! waiting for more.Anonymous
May 18, 2009
Ah a question! can we use mocking?!Anonymous
May 19, 2009
This is good. I'm especially happy to see the increased support for LINQ and other methods in LINQ to Entities; this is the single biggest issue for me with regards to testability right now. I would love it if the Entity Framework would automatically generate a mocked ObjectContext. But any effort that you spend improving LINQ support is even more valuable to me.Anonymous
May 19, 2009
Does this version support all the new SQL Server 2008 data types? Especially hierarchyid and geograpy types?Anonymous
May 19, 2009
What about: Being able to undo relationship changes easily? <-- huge View Generation improvements (adding all those strings together)? Underlying query method improvements? Easier access to FK values? I guess this might be solvable from a T4 perspective. Is there a better disconnected tier story?Anonymous
May 19, 2009
The comment has been removedAnonymous
May 19, 2009
@Kesav Unfortunately no. With all the other things we've been doing we couldn't get to things like HierarchyId and Geography etc. AlexAnonymous
May 26, 2009
Pick of the week: IP and Non-Competes for Employees General Visual Studio 2010 Beta 1 : Go get the first beta of the next version of Visual Studio! Microsoft Set To Announce Commercial Availability of Windows Azure at PDC This Year : Alin Irimie has someAnonymous
June 03, 2009
Introduction: From the moment I put my hands on Visual Studio.Net 2010 Beta 1 and I’m targeting EF4Anonymous
July 19, 2009
This is good. I'm especially happy to see the increased support for LINQ and other methods in LINQ to Entities; this is the single biggest issue for me with regards to testability right now. I would love it if the Entity Framework would automatically generate a mocked ObjectContext. But any effort that you spend improving LINQ support is even more valuable to me.Anonymous
August 24, 2009
How to make data across the n-Tier application? For example, WCF?Anonymous
November 04, 2009
Why doesn't Microsoft do as they done with ASP.NET and JQuery. Microsoft don't know O/R-mappers and seems to focus more on drag'n drop funktionalities. Why not begin to support Nhibernate instead?Anonymous
December 18, 2009
You are mentioning that "we have been working with Microsoft’s Patterns & Practices Team to produce new guidance and a Reference Implementation of this pattern (along with other patterns used Domain Driven Design) with Entity Framework" Where can I find this guidance?Anonymous
January 15, 2013
Honestly? I'm COMPLETELY at a loss as to how to unit test entity framework. I want just ONE example of it working. Anywhere. None of this makes sense at all. I can't extend or use DbSet or ObjectSet. I can't make a new DbSet or ObjectSet. How do you do it?!?Anonymous
January 15, 2013
@Vince - Here is a post with a working example romiller.com/.../testing-with-a-fake-dbcontext