Introducing Entity Framework Unit Testing with TypeMock Isolator

by mosessaur| 06 September 2009| 7 Comments

Introduction

One of the challenging things with current version of Entity Framework, its leakage of testability. Which means when you build an application or module (e.g. Repositories) that depends on entity framework, it will be very hard to unit test your code isolated from Entity Framework. This might lead to conduct Integration Testing which will require a connection and interaction with underlying data store (database).

You might think of using to mock or fake Entity Framework data store dependent calls. But sadly and Rhino Mock mocking frameworks do not support this. And you’ll end up hitting the wall. Both frameworks are great useful and I personally use for a while now and very happy with it. But no one can deny limitations when exist.

And personally I was able to overcome the limitation of the testability of Entity Framework while I was working in  Entity Framework based models and repositories. But done that through unnecessary code refactoring and wrappers around none mockable classes such as sealed class EntityCollection<T>.

Finding a solution with TypeMock Isolator

In fact there is a solution, that will save you lots of workarounds and unnecessary code refactoring. You just need a full isolation framework. Isolator provides this capability. It allows to mock sealed classes and classes that have private constructors, static methods, and much more.

makes it easy to simulate and break the dependencies of any object in your existing code. In that way, it doesn’t force you to think about how your design might need to change. –The Art of Unit Testing book page 132-

So, TypeMock isolator is a perfect mate for testing code that has dependencies on Entity Framework as how I am going to show now.

The Sample, snippet from KiGG

I am going to demonstrate how to use TypeMock isolator with Entity Framework through reflected model from project. Will use some code snippets from it and present single Repository class (class under test) and its class code. I’ll be using as my Unit Testing framework for simplicity.

Entity Framework Generated ObjectContext

After generating part of KiGG using Entity Framework wizard, an ObjectContext class is generated and it I called it KiGGEntityContainer. I added/enhanced the functionality of generated class by adding few methods such as InsertOnSubmit<TEntity>(TEntity entity) and DeleteOnSubmit<TEntity>(TEntity entity) methods.

public void InsertOnSubmit<TEntity>(TEntity entity)
    where TEntity : EntityObject
{
    string entitySetName = GetEntitySetName(typeof(TEntity));
    AddObject(entitySetName, entity);
}

public void DeleteOnSubmit<TEntity>(TEntity entity)
    where TEntity : EntityObject
{
    DeleteObject(entity);
}

In the above InsertOnSubmit method it calls a helper method GetEntitySetName. This method retrieve EntitySet name from the meta data.

Nothing to talk about more in this class, The test of this class is out of scope now as it will require access to EDM meta data. So for now it just directs calls to Entity Framework relevant methods AddObject & DeleteObject.

BaseRepository Class

A base generic class for all repositories. Very simple and contain basic functionality. There is nothing to test in this class as it has no logic at all. I’ll add its code for reference only and clarity.

public abstract class BaseRepository<TEntity>
    where TEntity : EntityObject
{
    private readonly KiggEntityContainer _context;

    protected BaseRepository(KiggEntityContainer context)
    {
        _context = context;
    }

    protected internal KiggEntityContainer DataContext
    {
        get
        {
            return _context;
        }
    }

    public virtual void Add(TEntity entity)
    {
        DataContext.InsertOnSubmit(entity);
    }

    public virtual void Remove(TEntity entity)
    {
        DataContext.DeleteOnSubmit(entity as TEntity);
    }
}

Model Class Under Test, Story class

Story is an entity model class. It has 3 navigations properties linking it with other 3 entities (User, Category and Tag). 

Current version of Entity Framework doesn’t support transparent lazy loading. Only eager loading and explicit loading are supported. To over come this limitation, I marked the exiting navigation properties as internal. And made another properties that will handle the logic of loading related end if not already loaded just as the following:

public partial class Story
{
    public ICollection<Tag> Tags
    {
        get
        {
            if(!TagsInternal.IsLoaded)
            {
                TagsInternal.Load();
            }
            return TagsInternal;
        }
    }

    public Category Category
    {
        get
        {
            if(!CategoryInternalReference.IsLoaded)
            {
                CategoryInternalReference.Load();
            }
            return CategoryInternalReference.Value;
        }
    }
}

The above 2 properties will need to be tested as they contain logic. What I need to test here is when IsLoaded property of EntityCollection<Tag> (TagsInternal) Or EntityReference<Category> (Category) returns false, Load() method must be called. And if it return false, Load() method mustn’t be called. Here is the first test method –code is commented for clarification-

[Fact]
public void Tags_Should_Call_Load_When_IsLoaded_Is_False()
{
    // Arrange
    var story = new Story(); //Live Object
    //Fake the return of IsLoaded property of EntityCollection<Tag>
    Isolate.WhenCalled(() => story.TagsInternal.IsLoaded)
           .WillReturn(false);
    //Fake the call of Load method by ignoring the call.
    //Not doing so will throw an exception because
    //Load is supposed to load data from data store.
    Isolate.WhenCalled(() => story.TagsInternal.Load())
           .IgnoreCall();
    #pragma warning disable 168
    //Act: Issue call to Tags property
    ICollection<Tag> tags = story.Tags;
    #pragma warning restore 168
    //Assert: Verify that Load method has been called.
    Isolate.Verify
           .WasCalledWithAnyArguments(()=>story.TagsInternal.Load());
}

What exactly TypeMock Isolator provided me here?

  • It was impossible with any other Mocking framework to fake method calls on live object (real object not faked) or one of its properties. In this case the live object the story instance.
  • Faking EntityCollection<T> or any of its properties or methods also impossible with other Mocking frameworks, because the class is sealed! TypeMock totally isolate me from Entity Framework by giving the ability to fake Entity Framework types and dependent method calls such as calling IsLoaded property of EntityCollection<T> and Load() method.

As you see, TypeMock isolates my entity class test from Entity Framework although it depends on it. The rest of this StoryFixture test class is included in the attached sample.

The Class Under Test, CategoryRepository

In this class I have 5 methods which I need to test. 3 or them concerns with LINQ to Entities queries and the other 2 are for Add and Remove, both of these 2 methods contains logic that I must be tested. For sample I’ll introduce the Add method:

public override void Add(Category category)
{
    if (DataContext.Categories.Any(c => c.Name == categoryName))
    {
        throw new ArgumentException("Some Error Message...", "category");
    }

    category.UniqueName = UniqueNameGenerator.GenerateFrom(DataContext.Categories, category.Name);

    base.Add(category);
}

The Add method checks if the name exists in the data source or not. If exists an exception should be thrown. If not a unique name to be generated for the category and then to be added to current ObjectContext for later saving.

Issues with testing such method is that it depends on Entity Framework. And current version of Entity Framework will require a database hit to evaluate this query “DataContext.Categories.Any(c => c.Name == categoryName)”!!

Again TypeMock is a saver here. TypeMock is able to fake any ObjectContext instance. Which is not supported by other mocking engines. Faking ObjectContext instance and having the ability to fake all of its method calls and properties is really great thing. I missed that with Moq!

First test for this method is to check if an exception is thrown when a new category is added and there is existing one with the same name –Code commented for clarification-:

[Fact]
public void Add_Existing_Category_Should_Throw_ArgumentException()
{
    //Fake KiggEntityContainer -ObjectContext-
    var context = Isolate.Fake
                      	.Instance<KiggEntityContainer>(Members.ReturnRecursiveFakes);

	//Fake call to dependecy static method on static class UniqueNameGenerator
    Isolate.Fake
           .StaticMethods<UniqueNameGenerator>(Members.ReturnRecursiveFakes);

    var repo = new CategoryRepository(context);
	//Create In memory list of categories
    var inMemoryCategories = new List<Category>
               							{
                   						new Category{Name = "Dummy1"}, 
                   						new Category{Name = "Dummy2"}
               							};
    
    //Fetch existing category name
    string name = inMemoryCategories[0].Name;

	//Fake the return of ObjectQuery<T> "context.Categories"
	//Instade of using physical datastore I'm using In Memory Store
	//LINQ to Entites will be simulated with LINQ to Objects
    Isolate.WhenCalled(() => context.Categories)
           .WillReturnCollectionValuesOf(inMemoryCategories.AsQueryable());

	//Assert 
    Assert.Throws(typeof(ArgumentException), 
									() => repo.Add(new Category { Name = name }));
}

Line 5 using fluent AAA (Arrange-Act-Assert) TypeMock APIs to fake an instance -Mock- of KiggEntityContainer object context class. If you tried to do this with Moq for example you’ll desperately receive and exception that EDM meta data couldn’t be loaded. Note here that I also don’t care about the constructor parameters. I like this total isolation.

Line 9 I am faking –subbing- a call to as static method on some static class. I just need to fake its call, and this test doesn't really care about its returned value

Line 14 I am creating an in memory collection of categories. which will be used in replacement for underlying data store as we will see.

Line 26 the most important line. In this line the powerful of TypeMock shown. Here I’m able to fake the return value of ObjectQuery<Category> “context.Categories”. So simply they like says when calling context.Categories return the in memory collection of categories “inMemoryCategories”. There is a trick on this line of code to make it work; I have to call AsQueryable() of the replacement collection. If I didn’t I’ll receive and exception.

Finally Line 30, Asserting that an exception is thrown when call Add method of CategoryRepository instance when passing a new Category with a name that match an existing category in the underlying data source.

Another method in CategoryRepository which is FindByUniqueName is used to retrieve a category by its unique name here is the method:

public Category FindByUniqueName(string uniqueName)
{
	return DataContext.Categories.FirstOrDefault(c=>c.UniqueName == uniqueName);
}

Very simple method with no logic! So what exactly I need to test here? Zooming into code, you’ll notice that I want to a category when its unique name matches a specific input. I want to test this LINQ expression that it yields the correct results.

Here is the test method

[Fact]
public void FindById_Should_Return_Correct_Category()
{
    //_context is a fake ObjectContext instance
    var repository = new CategoryRepository(_context);
    
    //In memory collection store of categories
    List<Category> inMemoryCategories = new List<Category>
               				{
                   			new Category{Id=Guid.NewGuid(),Name = "Dummy1"}, 
                   			new Category{Id=Guid.NewGuid(),Name = "Dummy2"}
               				};
    
    Guid id = inMemoryCategories[0].Id;

    //Fake ObjectQuery<Category>
    //Must call AsQueryable() extension in order for this to work
    Isolate.WhenCalled(() => _context.Categories)
           .WillReturnCollectionValuesOf(inMemoryCategories.AsQueryable());
    
    //Act
    Category found = repository.FindById(id);

    //Assert
    Assert.Equal(id, found.Id);
}

Again I am replacing (faking) the return of ObjectQuery<Category> with in memory list of categories. This way when _context.Categories is called an in memory IQueryable<Category> will be returned. The LINQ will be evaluated as LINQ to Object an not as LINQ to Entities. Because LINQ to Entities will require a database hit which I want to avoid.

This way TypeMock Isolator provided an isolation from Entity Framework, although my component is totally dependent on Entity Framework. This is impossible with other mocking framework which shows the strength of TypeMock in this point.

Code for this sample downloadable from here. Note that I didn’t include TypeMock assemblies which you should download from

Conclusion

There is a white paper published by TypeMock with comparing different mocking framework to TypeMock itself. This comparison white paper is downloadable from here. Also there is an open source project to compare between mocking frameworks which you can check it out .

I’ve been working with different mocking framework for a while now. Moq is my favourite free open source one. I also worked a little with Rhino Mock, a great framework however at the time I was working Moq, I didn’t feel comfortable with record & replay mode of Rhino Mock. Beside Moq was very simple and very easy to learn. Rhino Mock is going to rock in its upcoming version 4.0 you can read about its plans .

TypeMock is the most powerful mocking framework I ever seen so far. as I mentioned earlier makes it easy to simulate and break the dependencies of any object in your existing code. In that way, it doesn’t force you to think about how your design might need to change. –The Art of Unit Testing book page 132-.

TypeMock can isolate almost everything, they even have specific release for unit testing with SharePoint which so far no tool compete on this with it.

TypeMock is commercial, However, they provide free license for open source projects. And for MVPs, they are eligible for a free version TypeMock Isolator Enterprise Edition! MVPs can check it out here.

 

Download the Sample

Comments (7) -

Daniel Simmons
Daniel Simmons United States on 9/2/2009 2:10 PM This post is great.  I'm glad to hear about success with TypeMock and the EF.  I'm assuming this is the first release of the EF, though.  Have you tried similar scenarios with IObjectSet and POCO support in EF4?  Presumably this process should be much easier and not require a big gun like TypeMock.  It would be great to hear if you try that at some point.

- Danny
Muhammad Mosa
Muhammad Mosa Egypt on 9/2/2009 8:28 PM Thank you Daniel. And yes, I had plans to try it with EF 4, just need to arrange sometime. I was aiming for first hit with EF v1 and it worked like a charm.
Once done with EF 4 and it is successful I'll for sure post about it.
Mike Bridge
Mike Bridge Canada on 9/21/2009 1:06 AM I'm not getting an exception when I don't use "AsQueryable()" with a List.  What kind of problem were you seeing?

I found that when I passed in a List<> with AsQueryable, Isolator didn't execute the IQueryable wrapper around the list in the faked query, and my Assertions would fail because it saw an empty list.  When I pass in a List<> without "AsQueryable()" it works ok....
Muhammad Mosa
Muhammad Mosa Egypt on 9/21/2009 5:29 AM This is weird! because when do this:
Isolate.WhenCalled(() => context.Categories)  .WillReturnCollectionValuesOf(inMemoryCategories);
And perform a query against context.Categories like this
context.Categories.FirstOrDefault(c=>c.Id == "someid");
I get an exception. I don't remember what is it exactly but I confirmed this with TypeMock. Then I made this approach of using AsQueryable and it worked for me! What version are you using of TypeMock?! also Are you testing against Entity Framework?
Muhammad Mosa
Muhammad Mosa Egypt on 9/21/2009 6:04 AM Ok again this is really weird! because I just test my sample again with latest release of TypeMock v5.3.5. And I am still getting the same exception! It is a NullReferenceException. Are you sure you are testing against Entity Framework?! Please try to test the sample attached to this post. And the exception take place upon executing the LINQ to Entities Query like iterating or calling FirstOrDefault or similar extension methods.
Mike Bridge
Mike Bridge Canada on 9/22/2009 2:05 PM Hi-

Yeah, I had a closer look at this and it's in the expression.Compile() call.  For whatever reason (this is a little beyond my area of expertise), the IQueryable compiles, but the List that gets constructed can't.  I suspect it's trying to dereference something in the EntityObject, but I'm not sure what it is, and why it works with an IQueryable and not a List<>.

Regardless, I think mine works just because I'm not passing it through a compilation step.

Cheers!

-Mike
Muhammad Mosa
Muhammad Mosa Egypt on 9/22/2009 5:00 PM It is failing not because of the compile! I did the same without compiling the query! it is still failing!
It is really weird! I want to investigate your code can you please send it to me through contact form?

Pingbacks and trackbacks (11)+

Comments are closed