Use a generator to test that pesky DataContext

There has been a lot of buzz about how exactly to get around the issues of testing the DataContext in LINQ to SQL.  I've seen a lot of good blog posts explaining very good solutions to the problem.  But so far I have yet to see a total solution written up yet.

The first thing I set out to do was create an ITable<T> interface, and a TableProxy<T> class so that I could properly wrap the Table<T> objects which come on a DataContext.

I created a simple class which takes a Table<T> into its constructor then simply passes through to the methods on that Table, this class implemented my new interface and voila.  Simple enough?  I also decided to make a TableFake<T> which would simply insert and remove from a predefined list, that way I could save myself a little work stubbing out ITable<T> for all of my tests.

Proxy Generation

The next trick was to create my IXXXDataContext and XXXDataContextProxy.  But this wasn't something I wanted to have to update manually every single time we added a new table or function call to the generated data context.  I started sifting through the DBML generated Xml file that is created to keep track of all the important database artifacts before creating the actual DataContext file itself.  It looked like it would be pretty simple to put together a small console application that could read this file, and spit out the interface and proxy into one file as partial's.

Testing Magic

The beauty of this approach can best be seen in the tests written against it.  Consider the following code:

public class SomeClass
{
    public IDataContext Context { get; set; }

    public void SaveBook(Book book)
    {
        if (book.IsNew)
           Context.Books.InsertOnSubmit(book);
        else
           Context.Books.Attach(book, true);
        Context.SubmitChanges();
    }
}

With your typical DataContext, the only way to test this would be if you were willing to actually save to some kind of database.  After generating the proxy however.  Our test will look as follows:

public class When_saving_a_new_book : Specification
{
    private IDataContext _context;
    private SomeClass _someClass;

    protected override Before_each_spec()
    {
        _context = Mock<IDataContext>();
        _context.Stub(o => o.Books).Return(new TableFake<Book>(new List<Book>()));
        _someClass.Context = _context;
        Mocks.ReplayAll();
    }

    public void Then_submit_changes_should_be_called()
    {
        _someClass.Save(new Book());
        _context.AssertWasCalled(o => o.SubmitChanges());
    }

    public void Then_the_new_book_should_be_added_to_the_table()
    {
        var _book = new Book();
        _someClass.Save(_book);
        _context.Books.Count().ShouldBe(1);
        _context.Books.ShouldContain(_book);
    }
}

All of the code for the TableProxy<T>, ITable<T>, TableFake<T> and the generator application can be found in the CodeIncubator samples on Google Code.  Feel free to use this information as you see fit and let me know what you think.

Get the Code!