Time for another guest post (check out others in the series), this time bringing together the world of mocking with the world of Entity Framework. A big thanks to Moses for agreeing to do this. Unit Testing Entity Framework Dependent Code using TypeMock Isolator by Muhammad Mosa Introduction Unit testing data access code in my opinion is a challenging thing. Let us consider unit tests and integration tests. In integration tests you are allowed to have environmental dependencies such as a physical database connection to insert, update, delete or retrieve your data. However when performing unit tests it is often much more efficient and productive to remove environmental dependencies. Instead you will need to fake these dependencies. Faking a database (also known as mocking) can be relatively straight forward but the version of Entity Framework released with .Net 3.5 SP1 has a number of implementation specifics which actually makes faking the existence of a database quite difficult. Faking Entity Framework As mentioned earlier, to effectively unit test you will need to fake/simulate Entity Framework calls to the database. There are many free open source mocking frameworks that can help you achieve this but it will require additional effort to overcome & workaround a number of limitations in those frameworks. Examples of these limitations include: Not able to fake calls to non virtual methods Not able to fake sealed classes Not able to fake LINQ to Entities queries (replace database calls with in-memory collection calls) There is a mocking framework which is flexible enough to handle limitations such as those above. The commercially available TypeMock Isolator can do the job for you with less code and ultimately more readable unit tests. I’m going to demonstrate tackling one of those limitations using MoQ as my mocking framework. Then I will tackle the same issue using TypeMock Isolator. Mocking Entity Framework with MoQ One basic need when faking Entity Framework is to fake the ObjectContext. This cannot be done by passing any connection string. You have to pass a correct Entity Framework connection string that specifies CSDL, SSDL and MSL locations along with a provider connection string. Assuming we are going to do that, we’ll explore another limitation. The limitation we are going to face now is related to not being able to fake calls to non-virtual/overridable members with MoQ. I have the following repository method that adds an EntityObject (instance of a Blog entity) to Blogs entity set in an ObjectContext. public override void Add(Blog blog)
{
if(BlogContext.Blogs.Any(b=>b.Name == blog.Name))
{
throw new InvalidOperationException("Blog with same name already exists!");
}
BlogContext.AddToBlogs(blog);
}
The method does a very simple check that the name of the new Blog entity instance doesn’t exist. This is done through the simple LINQ query above. If the blog doesn’t already exist it simply adds it to the current context to be saved when SaveChanges of the ObjectContext instance (e.g. BlogContext) is called. However, if a blog with the same name exits, and exception (InvalideOperationException) will be thrown.
Let us now create a unit test for the Add method using MoQ.
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Add_Should_Throw_InvalidOperationException_When_Blog_With_Same_Name_Already_Exits()
{
//(1) We shouldn't depend on configuration when doing unit tests! But,
//its a workaround to fake the ObjectContext
string connectionString = ConfigurationManager
.ConnectionStrings["MyBlogConnString"]
.ConnectionString;
//(2) Arrange: Fake ObjectContext
var fakeContext = new Mock<MyBlogContext>(connectionString);
//(3) Next Line will pass, as ObjectContext now can be faked with proper connection string
var repo = new BlogRepository(fakeContext.Object);
//(4) Create fake ObjectQuery<Blog>. Will be used to substitute MyBlogContext.Blogs property
var fakeObjectQuery = new Mock<ObjectQuery<Blog>>("[Blogs]",
fakeContext.Object);
//(5) Arrange: Set Expectations
//Next line will throw an exception by MoQ:
//System.ArgumentException: Invalid setup on a non-overridable member
fakeContext.SetupGet(c=>c.Blogs).Returns(fakeObjectQuery.Object);
fakeObjectQuery.Setup(q => q.Any(b => b.Name == "NewBlog")).Returns(true);
//Act
repo.Add(new Blog { Name = "NewBlog" });
}
This test method is checking to see if the correct exception ([ExpectedException(typeof(InvalidOperationException))]) is thrown when a developer attempts to Add a blog with a name that’s already exists.
On (1) a connection string is initialized from configuration file. To retrieve the full connection string.
On (2) a fake ObjectContext is being created. The ObjectContext here is MyBlogContext and its being created using this var fakeContext = new Mock<MyBlogContext>(connectionString); This way a fake context is being created using MoQ.
On (3) a BlogRepository instance is created. BlogRepository has dependency on generate Entity Framework ObjectContext, MyObjectContext. And so the fake context is passed to the constructor.
var repo = new BlogRepository(fakeContext.Object);
On (4) a fake instance of ObjectQuery<Blog> is being created to use as a substitute to MyObjectContext.Blogs property as we will see in (5).
On (5) setup an expectation for calling Blogs property of MyBlogContext and substitute the return result with the fake ObjectQuery<Blog> instance created on (4).
When you run this test it will fail with MoQ throwing an exception because of this line:
fakeContext.SetupGet(c=>c.Blogs).Returns(fakeObjectQuery.Object);
This happens because the generate property MyBlogContext.Blogs is not virtual/overridable. And assuming it is virtual or you managed to make it virtual it will fail at the following line throwing the same exception:
fakeObjectQuery.Setup(q => q.Any(b => b.Name == "NewBlog")).Returns(true);
This time the test will fail because the Any extension method is not virtual/overridable. You won’t be able to replace ObjectQuery<Blog> with fake in memory collection to test your LINQ to Entities queries.
Now lets see how replacing MoQ with TypeMock Isolator can help.
Mocking Entity Framework with TypeMock Isolator
The following is the same test method we had above for MoQ but this time implemented using TypeMock Isolator:
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Add_New_Blog_That_Already_Exists_Should_Throw_InvalidOperationException()
{
//(1) Create fake in memory collection of blogs
var fakeInMemoryBlogs = new List<Blog> {new Blog {Name = "FakeBlog"}};
//(2) create fake context
var fakeContext = Isolate.Fake.Instance<MyBlogContext>();
//(3) Setup expected call to MyBlogContext.Blogs property through the fake context
Isolate.WhenCalled(() => fakeContext.Blogs)
.WillReturnCollectionValuesOf(fakeInMemoryBlogs.AsQueryable());
//(4) Create new blog with a name that already exits in the fake in memory collection in (1)
var blog = new Blog {Name = "FakeBlog"};
//(5) Instantiate instance of BlogRepository (Class under test)
var repo = new BlogRepository(fakeContext);
//(6) Acting by adding the newly created blog ()
repo.Add(blog);
}
When running the above test method it will pass as the Add method of BlogRepository is going to throw an InvalidOperationException which is the expected behaviour. Nothing prevents us from faking out the database interaction! Even faking ObjectContext at (2) didn’t require a connection string. On (3) Isolator sets up a faking result for MyBlogContext.Blogs when its being called through the fake instance fakeContext created on (2). The faking result is just an in-memory collection declared an initialized on (1).
Finally at (6) action we call the Add method of BlogRepository passing a new Blog instance that has a name that’s already exists in the fake in-memory collection which we set up at (1). As expected the test will pass because it will throw the expected exception defined on top of the test method - InvalidOperationException.
TypeMock Isolator succeeded in faking Entity Framework with ease.
Conclusion
We explored how to write a simple unit test using TypeMock Isolator for code which is using Entity Framework. We also explored a few of the limitations of other mocking frameworks which TypeMock is successfully able to handle. There are workarounds that you can use to overcome limitations when using MoQ or Rhino Mock, however the workarounds will require you to write more code and your tests will likely be more complex.
For a comparison between different mocking frameworks take a look at this document produced by TypeMock. You might also want to check out this open source project to compare mocking frameworks.
I hope you enjoyed this post
Muhammad Mosa
http://mosesofegypt.net/
http://twitter.com/mosessaur
Screencast of unit testing Entity Framework
Related Links
GuestPost: Introduction to Mocking
GuesPost: Typemock Isolator – Much more than an Isolation framework