Test Driven Evolutionary Design with Entity Framework

NOTE: I am not an Entity Framework expert nor do I claim to be. I am a user of ORMs in the general developer space and this exercise is an attempt to map my workflow and knowledge to EF and it's associated tools. Here there be dragons.

Introduction

I have been an ORM fan for many years using open source projects in the NHibernate/Castle ecosystem. I try to push my development experience as close as I can get to the Ruby on Rails way. Combining NHibernate, Fluent NHiberate and Fluent Migrator, I am able to achieve level of malleability that allows me to evolve my data storage needs around the growing discovery of the business problem at hand. Despite getting a "thank you" in Julia Lerman's book on it, I have not taken Entity Framework for a serious spin because I did not feel I could achieve the same level of malleability and persistence ignorance. With the recent release of Entity Framework Migrations the last missing piece has been added and EF is now on feature parity with my beloved OSS stack.

I wanted to build something real with Entity Framework, so I came up with a simple web application to drive the project. I am going to create a simple web site that allows a user to enter their first name, last name and email address to sign up for a news letter. The system should verify that the email has not already been registered and send the registered user a validation email. The validation email should contain a link the user can click to validate the email address goes to a real person. This is a fairly simple application with a little bit of complexity to keep it interesting.

Spiking On Data Access

To get started, I know I will need to be able to save the users details. I realize this might be an odd place to start but it allows me to get to the interesting data access bits that have me curious. I start my solution with a Core library where all my core business logic will live. I like to have this assembly be completely environment agnostic, so you will notice that all dependent assemblies in the solution tend to point in toward this one. Here is where I will put my User object.

namespace TestingEntityFramework.Core
{
    public class User
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
    }
}

I know that my current goal is to be able to persist a user object and retrive it. So, I want to clearly state that goal in the form of a unit test. The idea here being that the unit test describes a portion of the behavior of the system and then that test is run thought the development of the application ensuring that the behavior still works the way we specified it should. These are of course basic Test Driven Development principals. So, I create my next assembly Tests, fire up the Nuget Package Manager Console and get a test environment set up using the following commands.

install-package NUnit
install-package NSubstitute
install-package Shouldly

These are the standard three testing tools I typically start out with. NUnit is my default unit testing system, Shouldly adds a nice fluent assertion syntax over the top of NUnit asserts and NSubstitute is my current favorite mocking framework. I don't have a need for NSubstitute yet, but the style of testing I do generally leads me to it. So I add it by default. Now to make sure my testing environment is all ready to go, I add my first test.

using NUnit.Framework;
using Shouldly;

namespace TestingEntityFramework.Tests
{
    [TestFixture]
    public class RealityTests
    {
        [Test]
        public void true_should_be_true()
        {
            true.ShouldBe(true);
        }
    }
}

Hitting F1 shows me that everything compiles, test run and I am ready to get going with my current goal. With Entity Framework the unit of work concept is wrapped up in the DbContext type. Typically a developer will inherit from DbContext and modify to expose aggregate roots. So I know that I will need to create a DbContext to be able to save my User instance. I think I understand enough to get my first test written.

using NUnit.Framework;
using TestingEntityFramework.Core;

namespace TestingEntityFramework.Tests
{
    [TestFixture]
    public class UserPersistenceTests
    {

        private DataContext context;

        [Test]
        public void can_persist_user()
        {
            var user = new User { Id = -1, FirstName = "Dirk", LastName = "Diggler", Email = "dirk@diggler.com"};

            context.Users.Add(user);
            context.SaveChanges();

            Assert.That(user.Id, Is.EqualTo(1));
        }
    }
}

This test fails to even compile because I do not have a DataContext type in the system yet. To get the test to compile I need to create that type. So I add my third assembly to the solution, Data. The goal with the Data assembly is to completely encapsulate persistance concerns. It will have a dependency on the Core library but the Core library will have no knowledge of Data. I know that I will be using Entity Framework and Migrations, I can add them to the assembly via NuGet with the following command.

console
install-package entityframework.migrations


This will reach out to the NuGet server and download the migrations assemblies as well as check it's dependencies and pull them as well. So I end up with the latest version of Entity Framework including Code First and Migrations. The Migrations package includes some initialization code that creates a Migrations folder in my Data project and adds a Configuration object that we will get to later. I can now add my DataContext type to the Data project, It looks like this.

```csharp
using System.Data.Common;
using System.Data.Entity;
using TestingEntityFramework.Core;

namespace TestingEntityFramework.Data
{
    public class DataContext : DbContext
    {
        public DbSet Users { get; set; }
    }
}

My unit test will now compile and fail with a null reference exception on the calls to the DataContext object. I need some way to construct a DataContext object that has been configured for unit testing purposes.

Now I am attempting to unit test actual data access, this is generally considered an anti-pattern in unit testing circles. If I need to actually have an instance of sql server running with a database that I can communicate with that is set up with perfect test data to run my test against this is going to add a large level of complexity and brittleness to my testing. How do I interpret a failing test that failed because data was missing or the sql server was down for maintenance?

The idea solution for this would be to have a way to spin up a database instance, push my schema into it, add test data and then execute my tests against it. Optimally this process should happen for every test case and be very fast. In the OSS stack I mentioned above I would accomplish this with a combination of Fluent Migrator, Sqlite and Fluent NHibernate. Here I plan to tackle it with Code First, SqlCE and EF Migrations.

I'll start by creating a migration for my Users table. Migrations are a way of declaratively describing a set of schema operations like creating tables, columns and keys and indexes. Migrations typically have two methods Up and Down. The up method applies the database changes to upgrade the database to the latest version an the down method removes those changes in the event of a downgrade. My User migration looks like this.

using System.Data.Entity.Migrations;

namespace TestingEntityFramework.Data.Migrations
{
    public class AddUser : DbMigration
    {
        public override void Up()
        {
            CreateTable("Users", b => new
            {
                Id = b.Int(nullable: false, identity: true),
                FirstName = b.String(nullable: false, maxLength: 255),
                LastName = b.String(nullable: false, maxLength: 255),
                Email = b.String(nullable: false, maxLength: 255),
            })
                .PrimaryKey(t => t.Id);
        }

        public override void Down()
        {
                DropTable("Users");
        }
    }
}

This migration will create a new table called Users with four columns Id, FirstName, LastName & Email with Id being the primary key.

Next up I need to tell Code First how to map my User type to the Users table defined by my migration. I can do this by modifying The DataContext and overriding the OnModelCreating method to look like this.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity().HasKey(x => x.Id)
                .ToTable("Users");

            base.OnModelCreating(modelBuilder);
        }

Notice that I am leaving a lot of information here out. I am simply telling Code First that the entity type User has a key Id and should be saved to the Users table. I am taking advantage of Code First's default conventions. It will figure out what to do with all the other columns based on their type and names base on sensible conventions. For instance the User property FirstName will be saved in the column FirstName because the types and names match. This is a feature of Fluent NHibernate that I love and am glad that EF is following the same model. This allows me to focus on functionality instead of writing reams of code to specify every detail of persistence. I hope that the conventions in EF are as easily overridden as FNH because it is truly a killer feature. See my previous post on the topic for more details.

Now that I have my migrations and mapping defined I need a way to combine them and create a DataContext that communicates with an instance of SqlCE database. Back in my Test library, I add a dependency to SqlCE via NuGet with the following command.

install-package sqlservercompact

This adds a bin deployable version of SqlCE to my project that does not require any installs. This is an awesome change in SqlCe as well. In the past I have done this with Sqlite and was bitten by odd differences between Sql Server and Sqlite, but the friction added by switching to SqlCE was to great due to required installations. The latest bits removes that friction completely which is great. The only advantage that Sqlite has in this situation now is it's ability to create in memory databases which are insanely fast which is a good thing from a unit testing stand point. SqlCE forces us to use a file which adds the overhead of IO communication into your tests. Please SqlCE team, make in memory databases happen.

With all the pieces available I can now create a factory to build up my test data context.

using System;
using System.Data.Common;
using System.Data.Entity.Migrations;
using TestingEntityFramework.Core.Extensions;
using TestingEntityFramework.Data;
using TestingEntityFramework.Data.Migrations;

namespace TestingEntityFramework.Tests.Helpers
{
    public class TestDataContextFactory
    {
        private const string ConnectionString = "Data Source={0}.sdf";
        private const string ProviderName = "System.Data.SqlServerCe.4.0";

        public static DataContext Build()
        {
            var databaseName = DateTime.Now.Ticks.ToString();
            StandUpTestDatabase(databaseName);
            return CreateDataContext(databaseName);
        }

        private static DataContext CreateDataContext(string databaseName)
        {
            var connection = DbProviderFactories.GetFactory(ProviderName).CreateConnection();
            connection.ConnectionString = ConnectionString.FormatWith(databaseName);
            return new DataContext(connection);
        }

        private static void StandUpTestDatabase(string databaseName)
        {
            var config = new Configuration
                             {
                                 ConnectionString = ConnectionString.FormatWith(databaseName),
                                 ConnectionProviderName = ProviderName,
                                 AutomaticMigrationsEnabled = true
                             };

            new DbMigrator(config).Update();
        }
    }
}

This factory generates a random database name, creates a database connection object for SqlCE, runs all migrations on it and then constructs a DataContext and returns it. Now all I need to do is wire it up in my persistance unit test like this.

using NUnit.Framework;
using TestingEntityFramework.Core;
using TestingEntityFramework.Data;
using TestingEntityFramework.Tests.Helpers;

namespace TestingEntityFramework.Tests
{
    [TestFixture]
    public class UserPersistenceTests
    {

        private DataContext context;

        [SetUp]
        public void SetUp()
        {
            context = TestDataContextFactory.Build();
        }

        [Test]
        public void can_persist_user()
        {
            var user = new User { Id = -1, FirstName = "Dirk", LastName = "Diggler", Email = "dirk@diggler.com"};

            context.Users.Add(user);
            context.SaveChanges();

            Assert.That(user.Id, Is.EqualTo(1));
        }
    }
}

Hitting F1, I discover I now have a passing unit test. Every run of this test fixture generates a completely new database file, runs migrations and saves a new user validating that all the pieces (migration & mapping) work as expected. During the process of developing this application, this test will get run thousands of times ensuring that as the application evolves persistance still works the way it is expected to work. With minimal effort deployment scripts can be generated based on the migrations or I can use the migrations themselves. This gives a high level of confidence in this data access layer that you usually do not get with traditional approaches.

Driving Functionality from the Outside In with Nancy

My original goal was to build an application, I have my data access spike complete and want to get my focus back on that goal. In my next post, I will discuss test driving web applications using the NancyFX framework. If you want to take a peek at what I am up to the source is currently evolving along with the source for this post.

Follow me on Mastodon!