Unit testing LINQ to SQL using TypeMock

04 May 2010

Recent months have brought about a proliferation of mocking frameworks that mocks what more traditional framework like Rhino Mocks cannot. Instead of creating and loading a mock implementation at runtime, the new breed of mocking frameworks hooks into the CLR to intercept and redirect calls. This opens up virtually every aspect of a class to mocking, which is useful for testing code not written with explicit testability in mind. Until recently, TypeMock was the only mocking framework around that took the latter approach, but it’s now being challenged by Moles from Microsoft Research and JustMock from Telerik.

Why traditional dependency-breaking techniques come short

After watching a screencast on how to use Moles to unit test LINQ to SQL without hitting the database, I thought it would be interesting to do the same with TypeMock. But first, let’s make sure we understand why traditional dependency-breaking techniques come short in testing LINQ to SQL. Assuming we want to put a repository under test, our goal is to mock how it accesses the database. Here’s a simple implementation of a repository that queries the Employee table of the AdventureWorks database:

public class EmployeeRepository {
    public List<Employee> GetEmployeesByHireDate(DateTime start, DateTime end) {
        using (var ctx = new AdventureWorksDataContext())
            return (from e in ctx.Employees
                    where e.HireDate >= start && e.HireDate <= end
                    select e).ToList();
    }
}

All calls to the database are routed through the AdventureWorksDataContext generated by Visual Studio. To mock access to the database, we therefore have to mock part of the data context. For the context doesn’t expose an interface that a fake can implement. In addition, the tables are accessed through properties on the context that return a type of Table<TEntity>. Unfortunately, the constructor of Table<TEntity> is internal and the class itself is sealed, eliminating the hope of instantiating or subclassing the type by traditional means:

public sealed class Table<TEntity> : IQueryProvider, 
        ITable, IListSource, ITable<TEntity>, IQueryable<TEntity>, 
        IEnumerable<TEntity>, IQueryable, IEnumerable
        where TEntity : class {
    internal Table(DataContext context, MetaTable metaTable) {
        ...
    }
}

For an example of how the data context itself creates an instance of Table<TEntity>, take a look at the Employees property on the AdventureWorksDataContext. It relies on the GetTable<Employee> method on the DataContext class to create an instance of Table<Employee>. Despite its constructors being internal, the GetTable<TEntity> method has no trouble constructing an instance of the Table<TEntity> type, as they both reside in the System.Data.Linq assembly:

public partial class AdventureWorksDataContext : DataContext {
    public Table<Employee> Employees {
        get {
            return GetTable<Employee>();
        }
    }
}

How to break the unbreakable

The design of LINQ to SQL leaves us short of a traditional testing seam, as Michael Feathers would phrase it; a place at which we can alter the behavior of a program without editing in that place. This explains why, with LINQ to SQL, traditionally we’ve had to test against a real database with all its constraints, making our tests brittle, slow, and painful to write and maintain. With the new breed of mocking frameworks the issues of not being able to subclass or not being able to call an internal constructor go away (and new issues take their place). Regardless, here’s how to write a unit test for the CustomerRepository that doesn’t hit the database:

[TestClass]
public class CustomerRepositoryTest {
    private EmployeeRepository _repository;

    [TestInitialize]
    public void Initialize() {
        _repository = new EmployeeRepository();

        var fakeEmployees = new List<Employee> {
            new Employee {EmployeeID = 1, HireDate = new DateTime(2004, 12, 1)},
            new Employee {EmployeeID = 2, HireDate = new DateTime(2006, 7, 1)},
            new Employee {EmployeeID = 3, HireDate = new DateTime(2009, 3, 1)}
        }.AsQueryable();

        var fakeDataContext = Isolate.Fake.Instance<AdventureWorksDataContext>();
        Isolate.Swap.NextInstance<AdventureWorksDataContext>().With(fakeDataContext);

        // var fakeEmployeeTable = Isolate.Fake.Instance<Table<Employee>>();
        // Isolate.WhenCalled(() => fakeDataContext.Employees).WillReturn(fakeEmployeeTable);
        // Isolate.WhenCalled(() => fakeEmployeeTable).WillReturnCollectionValuesOf(fakeEmployees);
        // or by transitivity
        Isolate.WhenCalled(() => fakeDataContext.Employees).WillReturnCollectionValuesOf(fakeEmployees);
    }
        
    [TestMethod]
    public void GetEmployeesByHireDate_should_return_hires_from_2008_until_present() {
        var employees = _repository.GetEmployeesByHireDate(new DateTime(2008, 1, 1), DateTime.Now);
        Assert.AreEqual(1, employees.Count());
        Assert.AreEqual(3, employees[0].EmployeeID);
    }
}

The test method itself looks exactly as if we’d been testing against a real database. The difference lies in the Initialize method, where we setup the fake data context and database contents. We instruct TypeMock to return the fake context in place of the real one inside EmployeeRepository. And whenever someone calls the Employees property on the fake context, we have TypeMock intercept the call and return a fake collection of type IQueryable<Employee>. We could’ve returned an instance of Table<Employee>, which implements IQueryable<Employee>, but in this case returning the collection is simpler and sufficient. Had we had more methods on our repository, we likely would’ve added additional rows to the Employee table and populated more of its columns.