Useful Moq Extension Method

I have been working with ASP .NET MVC and I use the Moq mocking library to help test the code I write.   Often in ASP MVC anonymous objects are passed around as function arguments.  This is especially common in calls to RouteUrl.  Since I want to be able to test this and verify that it is called correctly I wrote a handy extension method to make it easier called AsMatch.

To be able to test the UrlHelper (since it doesn’t have an interface or virtual methods) I wrote a simple wrapper around it with an interface to enable testing.  Using that with this extension method makes testing route generation a breeze.

Given a call to generate a Url like this:

var url = Url.RouteUrl("Show", new { projectName="matt" });

You can write a setup on your Mock of the Url helper using a similar syntax:

UrlHelperMock
.Setup(x => x.RouteUrl("Show",new{ projectName="matt"}.AsMatch()))
.Return("someUrlToTest");

Here is the code that defines the AsMatch extension method:

public static class ObjectMoqExtensions
{
    public static object AsMatch(this object @object)
    {
        return Match<object>.Create(testObject => DoObjectsMatch(@object, testObject));
    }

    private static bool DoObjectsMatch(object object1, object object2)
    {
        var props1 = ToDictionary(object1);
        var props2 = ToDictionary(object2);
        var query = from prop1 in props1
                    join prop2 in props2 on prop1.Key equals prop2.Key
                    select prop1.Value.Equals(prop2.Value);
        return query.Count(x => x) == Math.Max(props1.Count(), props2.Count());
    }

    public static Dictionary<string, object> ToDictionary(object @object)
    {
        var dictionary = new Dictionary<string, object>();
        var properties = TypeDescriptor.GetProperties(@object);
        foreach (PropertyDescriptor property in properties)
            dictionary.Add(property.Name, property.GetValue(@object));
        return dictionary;
    }
}
 

One thought on “Useful Moq Extension Method

Comments are closed.