Saturday 28 August 2010

Unit Testing Entity Framework 4.0: A-Z in 3 Easy Steps

This article gives you an ultra-fast run down on how to start Unit Testing in Entity Framework 4.0 using C#; applying a suitable Code Generation Item to your Entity Data Model (EDMX) file, and building a Unit Test Project in Visual Studio 2010.

No external test harness is required, it doesn't rely on having a Repository pattern being implemented and you don't need to buy any new products. Just Visual Studio 2010 Professional Edition, Premium Edition or Ultimate Edition out of the box.

Terminology
The EDMX Code Generation Item used in this article generates two ObjectContext classes. When we refer to the Vanilla ObjectContext, this is the same strongly typed ObjectContext that is generated from your EDMX by default. The Mocking ObjectContext we refer to is another context used only for unit testing, which implements the same strongly typed interface, and is also generated from your EDMX file.

More details on the architecture of the artifact generator are provided here.


Step 1: Set Up the Code Generation Item

These steps will change your EDMX Code Generation Item to the ADO.NET Mocking Context Generator. This is what makes your C# code from your Entity Data Model (EDMX) file.
  • Open your EDMX file, right click the background, and from the context menu select Add Code Generation Item.
  • Select Online Templates->Database->ADO.NET Mocking Context Generator:


  • Open your EDMX file, right click the background, and from the context menu select Properties.
  • In the Properties change Code Generation Strategy to None. This will turn off the default Code Generation Item.



Step 2: Ensure "Context Agnostic" Code

Context Agnostic means that your Business Logic code shouldn't be aware of what context class it is using. We need to ensure that any hard dependencies on your generated Vanilla ObjectContext are replaced with references to your new generated ObjectContext Interface instead.

Identify Context "Gnostic" Code

We need to find anywhere in your Business Logic Layer that references the vanilla concrete ObjectContext:

class MovieLogic
{
    private MoviesContext _context;     // Hard dependency on vanilla ObjectContext

    public MovieLogic()
    {
        _context = new MoviesContext();     // Hard dependency on vanilla ObjectContext
    }
}

Option 1: Delegate responsibility to the client

To remove the hard dependency, the simplest option is to ensure all ObjectContext references use the ObjectContext Interface instead, and take on the ObjectContext instance in their constructor:
class MovieLogic
{
    private IMoviesContext _context;    // Interface used instead: context agnostic

    public MovieLogic( IMoviesContext context )
    {
        _context = context;     // Instantiation responsibility is delegated to caller
    }
}
The Business Logic Client can then be re-engineered to instantiate a vanilla ObjectContext object for us when the Business Logic object is instantiated:
MovieLogic logic = new MovieLogic( new MoviesContext() );
Note
Your application will never instantiate a Mocking Context, as this is only used when testing your business logic in your Unit Test Project.



Option 2: Delegate responsibility to a factory

If your Business Logic objects are instantiated all over the place, then it might be easier to contain the instantiation responsibility within a factory, and call the factory when a context is needed from the Business Logic objects themselves:
class MovieLogic
{
    private IMoviesContext _context;

    public MovieLogic()
    {
        _context = MyContextFactory.CreateContext();    
            // Instantiation responsibility is delegated to factory
    }
}
Implementing the Factory
Whilst no implementation of an example factory is given here, the factory just needs to return a new instance of your Vanilla ObjectContext, unless the "testing" flag is raised, when a Mocking ObjectContext will be instantiated instead.



Step 3: Create the Unit Tests

To create the unit tests in Visual Studio, a new test project needs to be created. This project will contain the Unit Test class that runs all the tests, and the code required to set up the mocking context, mock data, and the calls to the business logic.
  • Right click your solution, and select Add -> New Project
  • From the Add New Project Dialog select Visual C# -> Test -> Test Project:


  • Add a Reference in your new test project to your Business Logic Project.
  • Add a Reference to System.Data
  • Add a Reference to System.Data.Entity

Set up the test "database"

This step creates a fresh mocking context before every test is executed. The mocking context is populated with some sample data that the tests can use. Additionally any test can set up its own data as part of its testing.
  • In your new test project, open your Unit Test code file (eg. "UnitTest1.cs")
  • Add a member (eg. _context) to your unit test class, of the type of your ObjectContext Interface. The name of the interface will be shown if you expand the *.Context.tt file in the project containing your EDMX file:

  • private IMoviesContext _context;
  • Add a new function with the TestInitialize attribute. This will cause your function to be executed before every test. Create a mock context and fill it with your mock data. The name of the mocking context class will be shown if you expand the *.Context.tt file in the project containing your EDMX file:

  • [TestInitialize]
    public void TestInitialize() 
    { 
        _context = new MoviesContextMock();
    
        Actor[] actors = new Actor[]
        {
            new Actor() { Id = 1, Name = "Robert De Niro" },
            new Actor() { Id = 2, Name = "Ray Liotta" },
            new Actor() { Id = 3, Name = "Joe Pesci" },
            new Actor() { Id = 4, Name = "Jodie Foster" }
        };
    
        Movie[] movies = new Movie[]
        {
            new Movie() { Id = 1, Title = "Goodfellas",  ReleaseYear = 1990, Rating=5 },
            new Movie() { Id = 2, Title = "Taxi Driver", ReleaseYear = 1976, Rating=5 },
        };
    
        movies[ 0 ].Cast.Add( actors[ 0 ] );
        movies[ 0 ].Cast.Add( actors[ 1 ] );
        movies[ 0 ].Cast.Add( actors[ 2 ] );
        movies[ 1 ].Cast.Add( actors[ 0 ] );
        movies[ 1 ].Cast.Add( actors[ 3 ] );
    
        actors.ToList().ForEach( actor => _context.Actors.AddObject( actor ) );
        movies.ToList().ForEach( movie => _context.Movies.AddObject( movie ) );
    }


Implement Test Methods

Create some functions on your new unit test class, with the attribute TestMethod. These will execute the individual unit tests for your business logic.

Use the Assert.* methods to check the results of calls to your business logic. These calls will be evaluated by the test engine.

[TestMethod]
public void TestGetMovieByTitle()
{
    MovieLogic logic = new MovieLogic( _context );
    Movie movie = logic.GetMovieByTitle( "Goodfellas" );
    Assert.AreEqual( 1, movie.Id );
}

[TestMethod]
public void TestGetMovieByTitleBad()
{
    MovieLogic logic = new MovieLogic( _context );
    Movie movie = logic.GetMovieByTitle( "Arial the Little Mermaid" );
    Assert.AreEqual( null, movie );
}

[TestMethod]
public void TestGetMovieByReleaseYear()
{
    MovieLogic logic = new MovieLogic( _context );
    Movie[] movies = logic.GetMovieByReleaseYear( 1976 );
    Assert.AreEqual( 1, movies.Length );
    Assert.AreEqual( "Taxi Driver", movies[ 0 ].Title );
}



Running the Tests

To run the tests, select Test -> Run -> All Tests In Solution.
The Test Results panel will pop up, and give you the results of your tests:



Frequently Asked Questions

Where's my Vanilla ObjectContext's AddToObjectSetName method gone?

This method has now been deprecated in Entity Framework 4.0. Instead of using AddToObjectSetName, use <My Vanilla ObjectContext>.<ObjectSetName>.AddObject().

How do I unit test my EDMX EntityObject Functions?

Functions map to Stored Procedures in your database. You cannot, therefore, unit test your C# code and expect to call an SP as part of the unit test. You'll have to make sure that the high level logic function that calls the stored procedure is not included in your code C# tests; if you can break the function down into smaller method units within the logic class you may still be able to test the smaller logic units that don't require the SP. For unit testing the SP however, you need to create a Database Unit Test project.

How do I perform a unit test that includes database connectivity?

This, semantically speaking, is not possible. A code Unit Test should be testing units of code. If you need to perform a test across your code into a persistent storage medium, then this is an Integration Test. The Unit Testing phase is performed before the Integration Testing phase.

42 comments:

  1. Do you really think it necessary to unit test the database?

    ReplyDelete
  2. Hi Sean...

    No, in my opinion database unit testing is not something I would consider necessary as a general rule. If there are no SPs, functions or triggers, then there's nothing to unit test anyway.

    The only reason why I added a note about database unit testing was because some developers kept asking me; sometimes greying the boundary between the logic testing that I was advocating in this and my previous article.

    I have in the past unit tested databases, especially when there were a lot of or some very complex stored procedures. I've always just used a simple SQL script to do this (why require VS for a DB unit test?); I've never used the Visual Studio database unit test commercially.

    Rab

    ReplyDelete
  3. No, no, what I meant was that I feel that testing Entity Framework is in essence testing the database. I don't really see the value in that.

    Usually, I stop my unit testing when it hits my repository layer, I don't test the repository at all if it is just a thin wrapper over the data access method to the database.

    Granted, if the repository has complex logic within it, I'd unit test that; but generally it shouldn't.

    ReplyDelete
  4. Hi Sean -

    I see exactly where you're coming from now.

    Yes, you're absolutely right. If you have a repository pattern implementation with a concrete mocking implementation then following this article would be a duplication of responsibility; once you've successfully unit tested using your repository, all that you would be unit testing using this method is the repository itself, which would be testing overkill.

    The goal of this article and template is to get people into unit testing (with the lure of making it simple) by removing the burden of writing hand-crafted abstractions to implement mocking. I could have, instead of implementing a mocking ObjectContext generator, written a T4 script that generates a Repository & Unit of Work Pattern implementation with mocking.

    However, I didn't want to force the developer down the route of having an Repository/Unit of Work oriented paradigm. If they do implement it, then that's fine by me, but they still won't be required to do all the mocking implementation, as this is already abstracted, provided by this template.

    The Repository pattern, as far as I know, started making an appearance around .NET time, for which it was really useful. To paraphrase Hieatt/Mee: objects can be added and removed from a repository, and the repository takes care of the operations.

    But have you noticed that ObjectContext performs a very similar task? The ObjectContext is so similar in its interface that implementing a persisting concrete repository using Entity Framework turns into little more than 1-1 function mappings: Repository.Add -> ObjectContext.Add, Repository.Delete -> ObjectContext.Delete, etc.

    Additionally, with the deprecation of AddToObjectSetName, and the introduction of ObjectSetName.AddObject (too long coming), it's not even necessary to make discrete repositories for basic tasks.

    Again referencing Hieatt/Mee:

    Doesn't ObjectContext mediate between layers, acting "like an in-memory domain object collection"?

    Doesn't ObjectContext already perform the function of encapsulating mapping code?

    Doesn't ObjectContext expose collections that can be added to/removed from, and "carry out appropriate operation behind the scenes"?

    Plus with the implementation of this template, it's also possible to isolate the persisting concrete implementation, and replace it with a mocking concrete class.

    So if you take a step back, haven't you already got something very similar to a repository?

    Admittedly, objects aren't projectable by predicate function, but this is something that LINQ wraps up anyway. Also with the exposure of an IQueryable, along with the unavoidable necessity of the client using Include for eager loading, it could be argued that the ObjectContext does fall short of completely encapsulating query-centric tasks.

    Anyway, I'm not going to get roped into some Repository vs. ObjectContext holy war. I'm just trying to point out that this method of doing it is the closest thing to pleasing everyone.

    ReplyDelete
  5. Err sorry for the rant, Sean. I didn't realise how long it was until I posted.

    ReplyDelete
  6. :-)

    Not at all, I'm quite a ranter myself.

    I'm a fairly pragmatic boy, so I don't have a stick in a fight between ObjectContext and Repository.

    I agree with your points absolutely, I just think that anyone using your technique (which is very cool just btw), should be careful make sure they're testing their functionality, not the framework.

    ReplyDelete
  7. I realy like to see how did you use Repository with this template. I try it but i must modify some of template..

    Sorry for my English

    ReplyDelete
  8. Hi Florim -

    The idea behind the template is that it's got no difference from the standard code generation item and the context that's generated with this method.

    If you've got a repository then this should just be a "drop in" replacement. The only thing you need to remember is that you need to call SaveChanges() and other database specific functions on the vanilla concrete context itself. Calling these things on the interface won't work, as it has to be downcasted to the concrete context.

    However, as I pointed out to Sean in an earlier comment, you may want to take a step back and question if you need a repository at all when you switch to using this template.

    Regards
    Rab

    ReplyDelete
  9. Very nice work.

    @Sean: My 4 cents: DB test are slow. You may want to do fast integrations tests through multiple layers of your architecure. For example throwing Exceptions from persistence layer and see how business layer reacts. But also doing situations where persistence returns invalid data. I think this T4 template is a very nice starting point.

    ReplyDelete
  10. Thanks for the positive feedback anonymous!

    ReplyDelete
  11. Hi,
    Many thanks for this. Really great idea, and thanks for this post with the step by step instructions - worked first time!!

    thanks
    John

    ReplyDelete
  12. Hi Rab -

    Just to clarify, would this approach create a new .edmx file that my entire app would use, or a new .edmx file which would only be used for testing? I would prefer the second approach, as I my current .edmx has numerous entity mappings to sprocs in my database, and I use EntityKey in some generics that I built.

    ReplyDelete
  13. Nice job done! I started to edit the ADO.NET C# POCO Entity Generator until I realized that you already did most of the work.

    I changed the T4 template to my needs. Works like a charm, thanks for your efforts:
    http://bit.ly/bP8PtN

    ReplyDelete
  14. Nice article.
    I'm just starting with Entity Framework and testing and it helped me understand a few things.

    However what I still don't get is how can I create a mock if I'm using a partial class of my context to add some logic on the OnSaving method.
    So, to write really testable code should I avoid adding logic to entities partial classes?

    ReplyDelete
  15. It should be noted that if you are using Dynamic Data Entities using this will break editing ManyToMany Relations.

    ReplyDelete
  16. I've used partial classes a few times, and thought I was being really cool... but later on I've always needed to "move it up" to the business layer as it subtly made something break.

    My take would be to avoid partial ADO classes together. I wasn't aware that DDE breaks, but I seem to recall it caused problems with WCF Data Services.

    ReplyDelete
  17. This comment has been removed by a blog administrator.

    ReplyDelete
  18. Great template!

    However, to me there seems to be a problem with the default interface generation (IMoviesContext in your example):
    The generated interface does not implement IDisposable, which makes it impossible to have a using statement like
    using(var ctx=MyContextFactory.CreateContext()) returning just the interface.
    How about making the interface implement IDisposable, and give MoviesContextMock an empty Dispose() method?

    Regards
    Anders

    ReplyDelete
  19. Hi Anders -

    I'm so glad you like it. I'm a bit confused by your question though; why would you want to use the "using" keyword for something that has no resources to dispose? If you just want to scope your variable (ctx, in your example) you can use scope braces:

    {
    var ctx=MyContextFactory.CreateContext()
    ...
    }

    Regards
    Rab

    ReplyDelete
  20. Hi Rab!

    Perhaps my example was a bit unclear.
    It's absolutely true your mocking context has no resources to dispose, but the REAL ObjectContext-derived context does (thus ObjectContext implements IDisposable). In my example MyContextFactory.CreateContext() method returns an IMoviesContext interface, which could be either the mocking or the real context depending on the situation. You could use various mechanisms for this, personally I use the Unity's Locator feature to resolve the context.

    So I want IMoviesContext to derive from IDisposable, so that the object can be treated as disposable whether it is mock or real. Putting the code inside regular scope braces isn't enough, that would only control how far the object is reachable in code, but not trigger any resource disposal/garbage collection. Only the using-statement triggers the Dispose()-method, thus requiring that the objects/interfaces used there implements IDisposable.

    An empty IDisposable implementation in the mock object would only be used to make it "compatible" with the real context's disposing abilities, nothing else.

    Hope I made things a little clearer.

    Regards
    Anders

    ReplyDelete
  21. Great article and nice work!
    One question?
    Why not include methods like saveChanges() in the interface, and provide an empty implementation in the mock version.

    I started changing my code to use the interface, then quickly realized I can not call saveChanges when I need to. I have to do something like this.
    if ( _context is ConcreteContext)
    ((ConcreteContext)_context).saveChanges();
    in my businessLayer class.
    along with not able to call Dispose() in the code.

    I understand these methods are not needed when you are unit testing. But the code may also run in real scenario where it need to can the persistence functions.
    I have a feeling that I'm missing something critical here, or not?

    Thanks!
    Bo

    ReplyDelete
  22. I have tried using this template but not matter what I do, I can't get the object sets to add objects. No exceptions or anything. I am using my own POCO's but it shouldn't really matter. Any idea why object would not ever get added to the ObjectSet off of the context?

    ReplyDelete
  23. I just figured out my problem. Was using the vanilla context. Doh!

    ReplyDelete
  24. for SaveChanges()

    I did a extension method on IMoviesContext which will call ObjectContext.SaveChanges() if possible

    ReplyDelete
  25. +1 for the request for implementation of the IDisposable interface as requested by Anders

    ReplyDelete
  26. Not true that You shouldn't test repositories. They contain some logic. For example: repo.GetUpcomingTrips(). You have to test it if You are doing TDD.

    ReplyDelete
  27. I'm trying to test some relation code using this -- Lets say I've got posts and comments in the standard way.

    var p = new Post {Title = "One"};
    db.Posts.Add(p);
    p.Comments.Add(new Comment{Text="O Hai"});
    ....
    db.Comments.Count().ShouldEqual(1); //but actually equals 0...

    ReplyDelete
  28. Hi, I'm having the same problem as greg...
    Im creating a new EF Object connected to another one, but it isnt automatically connected to the mocked datacontext of the first one, while it does seem to work with the non-mocked version.

    ReplyDelete
  29. System.InvalidCastException : Unable to cast object of type 'Data.Models.EntitiesMock' to type 'System.Data.Objects.ObjectContext'

    ReplyDelete
  30. This comment has been removed by the author.

    ReplyDelete
  31. This comment has been removed by the author.

    ReplyDelete
  32. I found it handy to add a CreateIObjectSet() similar to the CreateObjectSet() so I could have a generic repository pattern. I modified my tt file, but it would have nice to have this in the project template.

    In the interface:
    IObjectSet < TEntity > CreateIObjectSet < TEntity > () where TEntity : class;

    In normal concrete context:
    public IObjectSet < TEntity > CreateIObjectSet < TEntity >() where TEntity : class
    {
    return CreateObjectSet < TEntity >();
    }

    In mock concrete context:
    public IObjectSet< TEntity > CreateIObjectSet< TEntity >() where TEntity : class
    {
    return new MockObjectSet < TEntity > ();
    }

    sorry about the spaces between the brackets. Looks like some kind of filter was taking those out of the comments.

    ReplyDelete
  33. Thank you for the article, it is great!

    I started using the extention for existing project that used to have default code generation context and entities for a while. Now I am at the point when I am trying to verify if application still runs as it used to.Now I am stuck on the problem when it is giving me null whenever I try to access any navigational property of an object retrieved from DB. The object itself has all db-inherited properties set right.

    Please if anyone came across the same issue - what's the solution to that?

    ReplyDelete
  34. This is just sweet!
    However I've run into some problems.

    I cannot use CreateMyEntity() or CreateSourceQuery():
    - MyEntity.CreateMyEntity(params)
    - MyEntity.CreateSourceQuery()

    Is there a workaround for this?

    ReplyDelete
  35. hi,
    Is ado.net mocking context generator template support for dbml file ?
    if answer will be no. How can i mocking dbml file?
    Thanks...

    ReplyDelete
  36. How to do function Import ? Is the template supports Function Imports?

    ReplyDelete
  37. I will do niche blog comment Just in 5$ .All comment relevant with your niche and UNIQUE .This off-page seo will increase your traffic and promote your business. HPT

    ReplyDelete
  38. Make the most of mainly premium substances - you will find him or her for: Viking, Sub-Zero, GE REFRIGERATOR REPAIR IN HIGHLAND

    ReplyDelete