Wednesday, January 02, 2008

Mocking Linq to SQL DataContext

If you're using Linq to SQL and you want to write unit tests, at some point you're going to have to mock the data context.  From what I can tell there's no real great way to do it without abstracting the data context into a factory-like class.

First, you can define your interface:

public interface IDataService
{
    IEnumerable<State> GetAllStates();
    State GetStateByAbbreviation(string abbreviation);
}

For your concrete interface:

public class SqlDataService : IDataService
{
    #region IDataService Members
 
    public IEnumerable<State> GetAllStates()
    {
        MVCPrototypeDataContext context = new MVCPrototypeDataContext();
 
        var query = from p in context.States select p;
 
        return query.ToList();
    }
 
    public State GetStateByAbbreviation(string abbreviation)
    {
        MVCPrototypeDataContext context = new MVCPrototypeDataContext();
 
        var query = from p in context.States where p.Abbreviation == abbreviation select p;
 
        return query.Single();
    }
 
    #endregion
}

Then for your mock interface:

public class MockDataService : IDataService
{
    private IList<State> states = new List<State> {
        new State { StateID = Guid.NewGuid(), Name = "Illinois", Abbreviation = "IL" },
        new State { StateID = Guid.NewGuid(), Name = "Wisconsin", Abbreviation = "WI" },
        new State { StateID = Guid.NewGuid(), Name = "Indiana", Abbreviation = "IN" }
    };
 
    #region IDataService Members
 
    public IEnumerable<State> GetAllStates()
    {
        return states;
    }
 
    public State GetStateByAbbreviation(string abbreviation)
    {
        return states.Single(p => p.Abbreviation == abbreviation);
    }
 
    #endregion
}

From here, you can use any IOC pattern or framework to configure the correct data service.

I really feel like having to wrap the Linq To SQL to support database-free unit testing takes the wind out of the sails of this feature.  This makes it so you need to make sure that your repository interface contains all of the possible methods you can imagine using for the business entity.

But the beauty of Linq to SQL is that you don't have to be bound to any of these static methods.  Having the power of building the database query through C# is what makes this feature great.

You can always take the "GetAll" method and perform some Linq to Entities, but then you loose the whole query portion of your OR/M layer, which to me, is extremely important.

I wonder if I spent more time on this if there's a better way to mock the data context.

1 comments:

CraigCav said...

I stubled across the following site whilst looking how to mock the dataContext. It seems to have a pretty clean mechanism: http://www.iridescence.no/Posts/Linq-to-Sql-Programming-Against-an-Interface-and-the-Repository-Pattern.aspx. Thought you might find it handy/interesting as well!