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.

Thursday 19 August 2010

Maintenance-Free Mocking for Unit Testing with Entity Framework 4.0

If you are trying to unit test in Entity Framework 4.0, you may have come across an apparent lack of architectural support for unit testing integration. Visual Studio 2010 appears to have excellent support for integrating Unit Test Projects into your solution, but no out-of-the box integration for using an Entity Framework Data Model (EDMX) generated Object Context is provided.

This article discusses how to implement a Mocking based approach to Unit Testing in a way that requires zero future code maintenance; generated automatically from the EDMX. The implementation doesn't require anything more than Visual Studio 2010. The finished product is implemented as a self-contained Visual Studio 2010 Item Template, available for download (see bottom of article).

Unit Testing & Mocking: A Brief Synopsis

If you want to unit test a Business Logic Layer, the BLL mustn't access the database within any part of the test cycle. If this happens the tests could be "contaminated" with errors from other layers. Additionally this sort of approach, whilst valid for full system testing, increases the overhead of performing the tests; a test database has to be set up, the schema implemented and the data put into a known state. A unit test only deals with testing on the component level, and shouldn't be dependent on persistent data, nor should it persist data itself.

Mocking is the process of swapping a functional object with a simulated mock object which shares the same interface. In the context of a BLL, this is normally the DAL "facade" or manager class; like your specialised ObjectContext class in Entity Framework. This mock object may then be provided to the test client in the stead of the functional class, and instead of retrieving and persisting data to the database, the mock object will gather data and/or report for testing purposes.

The Problem

The issue with attempting to "drop in" a mocking approach in Entity Framework is that your specialised ObjectContext class, the facade class generated from the EDMX, has no generated interface containing the specialised functionality. Without an interface, it's impossible to implement the classical approach to implementing a Mock ObjectContext:

From the class diagram shown you can see that MyObjectContext derives directly from ObjectContext; the only way to provide a Mock ObjectContext using the out-of-the-box architecture would be either a reflection or proxy-based approach, or by completely overriding the functionality in the base class (creating a refused bequest smell). All undesirable as they are complicated, potentially more error prone, and more difficult for a new developer on the team to pick up as they don't follow the classical Mocking approach.




The Solution

The solution is very simple, thanks to the new T4 (Text Template Transformation Toolkit) integration provided in Entity Data Modeller in Entity Framework 4.0. For the uninitiated, T4 is what is already used to generate your specialised ObjectContext and EntityObject classes from your EDMX file behind the scenes.

By using T4 we can instead come up with the following generated architecture, with very few changes to the out-of-the-box solution:

Note that MyObjectContext now implements IMyObjectContext. This allows the implementation of the MockMyObjectContext, and for it to share the same interface. This interface is passed to business logic clients instead of the concrete contexts, so that they are unaware if they are talking to the database, or instead being unit tested.

Download and Instructions

The download link and (very simple) guide to using is available here.


Thoughts and Considerations

Change Tracking and EntityObject
You may have noticed that the strongly typed entity object now derives from Object instead of EntityObject. This is because the entity object is now implemented as a POCO object as the complex change tracking interferes with the mocking. However, Entity Framework 4.0 employs generated proxy classes to manage change tracking if POCO objects' properties are implemented as virtual. See POCO in the Entity Framework.

ObjectQuery<T> Include
In the mocking context, ObjectSet<T> can no longer be used as this is a concrete object that liaises with the database. The context interface instead generates collections of type IObjectSet<T>, and both the concrete functional context and the mocking context implement this.

However, because ObjectSet<T> derives from ObjectQuery<T> this means we lose Include. To provide a workaround, the T4 generator implements an extension class for the interface IQueryable, that provides an Include method. This should mean that you can continue to use your existing code without any changes.

The workaround used was taken from Julie Lerman's Blog.

Wednesday 18 August 2010

ADO.NET Mocking Context Generator: Visual Studio 2010 Template

In my previous article, I discussed how to put together a Visual Studio 2010 Template to provide Maintenance-free Mocking for Unit Testing in Entity Framework 4.0.

The template is available for download from Microsoft, here.

You can also download it using Visual Studio 2010, by selecting Online Templates from the Add New Item dialog.

How to Use

Just like any other Entity Framework Artifact Generator, after installing:
  • Open your Entity Framework Model (EDMX) file
  • Right click the background and press Properties
  • Set Code Generation Strategy to None.
  • Right click the background again, and select Add Code Generation Item
  • From the Add New Item dialog, select ADO.NET Mocking Context Generator

    At some point I might extend this page to include a brief guide to unit testing. Until then, Julie Lerman's Blog is a good resource, along with the ADO.NET Team Blog.



  • Monday 9 August 2010

    Stateless paging in SQL Server CE 3.51 using Entity Framework

    If you are bound to Microsoft SQL Server Compact Edition 3.51 (or indeed, I think any of 3.x) using Entity Framework then you may have found the lack of support for server-stateless paging through queries:
    SELECT VALUE employee 
    FROM Employees
    ORDER BY employee.Name 
    SKIP(@skip) LIMIT (@limit)
    
    Both the LINQ Skip() and Take() extensions, along with the Entity SQL commands SKIP and LIMIT commands all come back with runtime errors.

    Paging with TOP

    This is an alternative SQL keyword that can be employed in SQL Server 3.51 CE. Normally it provides a function similar to LIMIT, although has no equivalent to SKIP, which makes it pretty useless. However, with a bit of fudging, it's possible to come up with an Entity SQL query that does the same thing:

    SELECT VALUE skip_query FROM
    (
        SELECT VALUE TOP (@count) take_query 
        FROM 
        (
            SELECT VALUE TOP(@end) employee
            FROM Company.Employees AS employee
            ORDER BY employee.Name ASC, employee.Id ASC
        ) 
        AS take_query
        ORDER BY take_query.Name DESC, employee.Id DESC
    ) 
    AS skip_query
    ORDER BY skip_query.Name ASC, employee.Id ASC;
    

    Considerations

    Admittedly, we've got the parameters count and end to use, so it's not exactly the same as SKIP and LIMIT, but it works just as well.

    If you're worried about efficiency, the improvements of properly paging should vastly overcome the use of nested statements and their performance impact on CE.

    Also, if you look at the documentation for TOP it does give a warning that it's non-deterministic. However note that as long as an ORDER BY clause is specified, this is negated. Do however note that each record must have a unique placement in the ORDER BY clause. I highly recommend putting the primary key as a secondary ordered field to ensure deterministicity, as shown in the example above.



    Source Code

    There's more than one way to implement an Entity SQL Query, but if you want implicit object materialisation, then using an ObjectQuery instance is the easiest way.

    private const string SkipLimitQuery = "SELECT VALUE skip_query...";
    private Employee[] GetEmployeesPaged( int start, int count )
    {
        using ( Company context = new Company() )
        {        
            ObjectQuery<DAL.Rom> query = context.CreateQuery<Employee>( SkipLimitQuery );
            command.Parameters.Add( new ObjectParameter( "count", count ) );
            command.Parameters.Add( new ObjectParameter( "end", start + count ) );
    
            return query.ToArray();
        }
    }

    Sunday 20 June 2010

    A Strongly Typed Expand Extension for WCF Data Services Clients

    For anyone that's used WCF Data Services (formerly ADO.NET Data Services), I guess you'll have run into the fact that using the Expand function for eager loading tables referenced by foreign keys uses a string as its overhead parameter:
    DataServiceQuery<Car> query = myDataServiceContext.Cars.Expand( "Wheels" );
    I was always really confused with this. This is what it's like in Entity Framework 4.0 as well with the Include function. Using a string literal of course means that schema changes will not be picked up at compile time. The code will successfully be compiled if the "Wheels" table has changed its name for instance, and the problem only gets picked up in the testing phase.


    A type safe approach

    So here's the code that I use. It's an extension class, so you can carry on using your existing data context object, and is not overtly "code-intrusive". It provides the ability to use strongly typed expressions instead of magic strings. When your schema changes, a compile error will be raised displaying that the foreign table name is no longer correct. This will of course make an automated build fail, which is exactly what we want.
    using DataServiceQueryExpressions;
    ...
    DataServiceQuery<Car> query = myDataServiceContext.Cars.Expand( car => car.Wheels );
    Note that you can also use the extension for generating cascading expand URIs, along with the ability to specify foreign key references across data collection boundaries, as shown below:
    DataServiceQuery<Car> query = myDataServiceContext.Cars;
    query = query.Expand( car => car.Engine );
    query = query.Expand( car => car.Engine.Manfucturer );
    query = query.Expand( car => car.Wheels );
    query = query.Expand( car => car.Wheels.First().Tyre );
    

    Source code

    Copy and paste the following code into a new class (.cs) file. Use the namespace from your business logic layer, and start expanding safely.
    // Rab Hallett 2009. Public Domain.
    // Whilst I'd appreciate if you could keep my name at the top of this source code,
    // I won't be mad at you if you rip it off and pass it off to your boss as your own.
    using System;
    using System.Text;
    using System.Linq.Expressions;
    using System.Data.Services.Client;
    
    namespace DataServiceQueryExpressions
    {
        /// <summary>
        /// Extension for the <code>DataServiceQuery</code> class that supplies an 
        /// override for the <code>Expand</code> function, allowing query expand 
        /// paths to be specified by strongly typed expressions as opposed to 
        /// magic strings.
        /// <example>
        ///     myObjectContext.MyNamedEntityCollection.Expand( x => x.ForeignKeyTable );
        /// </example>
        /// </summary>
        public static class ExpressionsExtension
        {
            /// <summary>
            /// Specifies the related objects to include in the query results.
            /// </summary>
            /// <returns>A new System.Data.Objects.ObjectQuery<T> with the defined 
            /// query path.</returns>
            public static DataServiceQuery<T> Expand<T>
                (this DataServiceQuery<T> parent, Expression<Func<T, object>> expression)
            {
                if ( expression.Body.NodeType != ExpressionType.MemberAccess )
                {
                    throw new ArgumentException( "expected a member access node for the " +
                        "expression body." );
                }
    
                const char PathSeparator = '/';
    
                // Get the last element of the include path
                MemberExpression expressionBody = (MemberExpression)expression.Body;
                StringBuilder path = new StringBuilder();            
                
                do
                {
                    // Build the next component of the include path
                    if ( path.Length > 0 )
                    {
                        path.Insert( 0, PathSeparator );
                    }
                    path.Insert( 0, expressionBody.Member.Name );
    
                    // Get the next expression node; throw if not a call or method type
                    Expression nextNode = expressionBody.Expression;
                    if ( nextNode.NodeType != ExpressionType.Call && 
                         nextNode.NodeType != ExpressionType.MemberAccess &&
                         nextNode.NodeType != ExpressionType.Parameter  )
                    {
                        throw new InvalidOperationException( 
                            "Unsupported node specified in expression: " + nextNode.NodeType );
                    }
    
                    // Skip any superfluous method calls (like First()): these are required
                    // for including through collection (eg. MyARecords.First().MyBRecord)                
                    while ( nextNode.NodeType == ExpressionType.Call )
                    {
                        nextNode = ( (MethodCallExpression)nextNode ).Arguments[ 0 ];
                    }
                    
                    // Get the next member expression, if any
                    expressionBody = nextNode as MemberExpression;
                }
                while ( expressionBody != null );
    
                // Call the underlying framework function with the path string
                return parent.Expand( path.ToString() );
            }
        }
    }