Using Mock Objects When Testing LINQ Code

I was wondering the other day whether LINQ could be used with NMock easily. One problem with testing code that has not been written to work with unit tests is that if you test business logic, you often end up making multiple round-trips to the database for each test run. With a very large test suite that can turn a few minute’s work into hours for a test suite. the best approach to this is to use mock data access components to dispense canned results, rather than going all the way through to the database.

After a little thought it became clear that all you have to do is override the IOrderedQueryable<T>.GetEnumerator() method to return an enumerator to a set of canned results and you could pretty much impersonate a LINQ to SQL Table (which is the IOrderedQueryable implementation for LINQ to SQL). I had a spare few minutes the other day while the kids were going to sleep and I decided to give it a go, to see what was involved.

I’m a great believer in the medicinal uses of mock objects. Making your classes testable using mocking enforces a level of encapsulation that adds good structure to your code. I find that the end results are often much cleaner if you design your systems with mocking in mind.

Lets start with a class that you were querying over in your code. This is the type that you are expecting to get back from your query.

public class MyEntity
{
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set { age = value; }
    }

    public string Desc
    {
        get { return desc; }
        set { desc = value; }
    }

    private string name;
    private int age;
    private string desc;
}

Now you need to create a new context object derived from the DLINQ DataContext class, but providing a new constructor function. You can create other ways to insert the data you want your query to return, but the constructor is all that is necessary for this simple example.

public class MockContext : DataContext
{
    #region constructors

    public MockContext(IEnumerable col):base("")
    {
        User = new MockQuery<MyEntity>(col);
    }
    // other constructors removed for readability
    #endregion
    public MockQuery<MyEntity> User;
}

Note that you are passing in an untyped IEnumerable rather than an IEnumerable<T> or a concrete collection class. The reason is that when you make use of projections in LINQ, the type gets transformed along the way. Consider the following:

var q = from u in db.User
        where u.Name.Contains("Andrew") && u.Age < 40
        select new {u.Age};

The result of db.User is an IOrderedQueryable<User> query class which is derived from IEnumerable<User>. But the result that goes into q is an IEnumerable of some anonymous type created specially for the occasion. there is a step along the way when the IQueryable<User> gets replaced with an IQueryable<AnonType>. If I set the type on the enumerator of the canned results, I would have to keep track of them with each call to CreateQuery in my Mock Query class. By using IEnumerable, I can just pass it around till I need it, then just enumerate the collection with a custom iterator, casting the types to what I ultimately need as I go.

The query object also has a constructor that takes an IEnumerable, and it keeps that till GetEnumerator() gets called later on. CreateQuery and CloneQueryForNewType just pass the IEnumerable around till the time is right. GetEnumerator just iterates the collection in the cannedResponse iterator casting them to the return type expected for the resulting query.

public class MockQuery<T> : IOrderedQueryable<T>
{
    private readonly IEnumerable cannedResponse;

    public MockQuery(IEnumerable cannedResponse)
    {
        this.cannedResponse = cannedResponse;
    }

    private Expression expression;
    private Type elementType;

    #region IQueryable<T> Members

    IQueryable<S> IQueryable<T>.CreateQuery<S>(Expression expression)
    {
        MockQuery<S> newQuery = CloneQueryForNewType<S>();
        newQuery.expression = expression;
        return newQuery;
    }

    private MockQuery<S> CloneQueryForNewType<S>()
    {
        return new MockQuery<S>(cannedResponse);
    }
    #endregion

    #region IEnumerable<T> Members
    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        foreach (T t in cannedResponse)
        {
            yield return t;
        }
    }
    #endregion

    #region IQueryable Members
    Expression IQueryable.Expression
    {
        get { return System.Expressions.Expression.Constant(this); }
    }

    Type IQueryable.ElementType
    {
        get { return elementType; }
    }
    #endregion
}

For the sake of readability I have left out the required interface methods that were not implemented, since they play no part in this solution. Now lets look at a little test harness:

class Program
{
    static void Main(string[] args)
    {
        MockContext db = new MockContext(GetMockResults());

        var q = from u in db.User
                where u.Name.Contains("Andrew") && u.Age < 40
                select u;
        foreach (MyEntity u in q)
        {
            Debug.WriteLine(string.Format("entity {0}, {1}, {2}", u.Name, u.Age, u.Desc));
        }
    }

    private static IEnumerable GetMockResults()
    {
        for (int i = 0; i < 20; i++)
        {
            MyEntity r = new MyEntity();
            r.Name = "name " + i;
            r.Age = 30 + i;
            r.Desc = "desc " + i;
            yield return r;
        }
    }
}

The only intrusion here is the explicit use of MockContext. In the production code that is to be tested, you can’t just go inserting MockContext where you would have used the SqlMetal generated context. You need to use a class factory that will allow you to provide the MockContext on demand in a unit test, but dispense a true LINQ to SQL context when in production. That way, all client code will just use mock data without knowing it.

Here’s the pattern that I generally follow. I got it from the Java community, but I can’t remember where:

class DbContextClassFactory
{
    class Environment
    {
        private static bool inUnitTest = false;

        public static bool InUnitTest
        {
            get { return Environment.inUnitTest; }
            set { Environment.inUnitTest = value; }
        }
        private static DataContext objectToDispense = null;

        public static DataContext ObjectToDispense
        {
            get { return Environment.objectToDispense; }
            set { Environment.objectToDispense = value; }
        }
    }

    public object GetDB()
    {
        if (Environment.InUnitTest)
            return Environment.ObjectToDispense;
        return new TheRealContext() as DataContext;
    }
}

Now you can create your query like this:

DbContextClassFactory.Environment.ObjectToDispense = new MockContext(GetMockResults());
var q = from u in DbContextClassFactory.GetDB() where ...

And your client code will use the MockContext if there is one, otherwise it will use a LINQ to SQL context to talk to the real database. Perhaps we should call this Mockeries rather than Mock Queries. What do you think?

About these ads

9 comments

  1. did u try the same with Visual Studio 2008? I am not able to do the same, as its implementing IQueryProvider and the CreateQuery too has two arguments – IQueryProvider and an Expression.

    Can you please give a link to your source code too? It would be really, really helpful for me

    Thanks

  2. I am really new to this, and to my shame seem to be able to understand only a very little of “extracting interfaces” and creating “Mock contexts”. I always thought I was a moderately capable coder, and recently decided to switch from VB to C#. On making the change, I also decided to do things “properly” – particularly the question of unit testing. I’ve got the gist of the principles of unit testing, but can’t get my head around Mocking.
    I have also realized that I am nothing more than a “hobby programmer” – a bit sad after doing this “part time” for so many years. I’m working my way though “Code Complete” but in the meantime would really appreciate it if you could point me in the direction of a good book, articles etc that will throw light on this for me? My particular interest is Linq for SQL.
    Many thanks in advance

  3. Hi Jeremy,

    Don’t be too discouraged – this is the product of a lot of digging around in the undergrowth of LINQ and C# 3.0!

    There are really several types of mock objects that you have at your disposal – Mocks, and Stubs are the two main types. the emphasis of unit testing is just that – to test the unit. If you test a (e.g.) business logic component, then you’ll end up testing it, plus any data access code you write, plus any SQL stored procs etc in the process. Using a mock object allows you to control the environment around the component you’re testing.

    The wikipedia article seems like a good jumping off point. I also found the book extreme programming tools for Java quite helpful.

    http://en.wikipedia.org/wiki/Unit_testing

    HTH, and good luck.

    Andrew

Comments are closed.