SyntaxHighlighter

Sunday 1 September 2013

Supporting Multiple Threads in Moq


This post was originally on my Posterous account, it's posted here without updating, some of the links my not work.

I'm a big fan of the Moq library.  It provides a really simple way of introducing mock objects into unit tests.  However recently we stumbled onto an issue with it.  The mocks that are produced are not thread safe.

 (See this issue)

Purests would say this isn't an issue as you shouldn't have threads in your unit tests, and this is a position I generally do agree with, however there is always some code to manage to multiple threads and that should be unit tested just as much as all the other code.

The issue has existed for a while and a colleague of mine has also created a fix, but the fix isn't in a released version and we didn't really want to go to the trouble of building Moq ourselves just for the one failng unit test.
One solution to this, is to make use of the same features Moq is built with.  Moq uses the Castle DynamicProxy library which is usually ILMerge'd with the Mod.dll assembly, but Moq also comes as a version without DynamicProxy merged in.  By switching to this version and using DynamicProxy ourselves we can do this:

public class SynchronizedInterceptor : IInterceptor
{
    private object lockObject = new object();

    public void Intercept(IInvocation invocation)
    {
        lock (lockObject)
        {
            invocation.Proceed();
        }
    }
}

public static class MoqSynchronisedExtensions
{
    private static readonly ProxyGenerator _generator = new ProxyGenerator();

    public static TType GetSynchronizedObject<TType>(this Mock<TType> mock) where TType : class
    {
        var synchronizedObject = _generator.CreateInterfaceProxyWithTarget<TType>(mock.Object, new SynchronizedInterceptor());
        return synchronizedObject;
    }
}

This allows to write this unit test:

[TestMethod]
public void Moq_WhenUsingSynchronizedObject_IsThreadSafe()
{
    // Arrange
    var m = new Mock<idisposable>();
    var disposeable = m.GetSynchronizedObject();

    // Act
    Parallel.For(0, 1000000, d =>
    {
        disposeable.Dispose();
        disposeable.Dispose();
        disposeable.Dispose();
        disposeable.Dispose();
        disposeable.Dispose();
        disposeable.Dispose();
        disposeable.Dispose();
        disposeable.Dispose();
    });
}
With this solution we are able to get our unit test to pass every time, without affecting any other tests, using a manual mock or resorting to our own version on Moq.

This solution might not work for everyone, GetSynchronizedObject() returns a new object every time, therefore it is only synchronized when accessed though that object.  Also if you are doing other synchronization in callbacks you may introduce deadlocks into your unit tests.

This should make a good stop gap if you are experiencing this issue until there is an official fix built into Moq.

No comments: