A few weeks ago I was working in a patch for an awesome mocking meta framework named mfakes, a very awesome mocking meta-framework based in the spicy Machine Specification BDD testing framework, go, try it, you are going to love it!.
Well, one of the awesome points in mfakes it’s to abstract mocking frameworks, so mfakes provides adapters for popular mocking frameworks like Rhino Mocks, Moq, NSubstitute and FakeItEasy. So instead of thinking which one to use, just concentrate in the test. Simple.
Anyway, there was a feature lacking in mfakes, and as every developer when something it’s lacking a feature you just go ahead and implement it, nothing beats open source. The interesting point was trying to “wrap” different way to interact between every mocking framework, everyone has a different way to see the world, it’s not bad, but it’s different. I found a very interesting problem with FakeItEasy and constructor parameters in a Mock.
In most mocking frameworks there’s an easy way to specify constructor parameters for a mock, in Moq, for example, you just do something like this:
var foo = new Mock<T>("foo", "bar");
// Generate this it's easy, it's just a simple generic type
// with a parameter list
public object CreateFake(Type interfaceType, params object[] args) {
var closedMockType = typeof(Mock<>).MakeGenericType(interfaceType);
var objectProperty = closedMockType.GetProperty("Object", closedMockType);
var instance = (args != null && args.Length > 0)
? Activator.CreateInstance(closedMockType, args) : Activator.CreateInstance(closedMockType);
return objectProperty.GetValue(instance, null);
}
The problem it’s that FakeItEasy it’s magic, and uses a very nice strong typed way to do it
var foo = A.Fake<FooClass>(x =>
x.WithArgumentsForConstructor(
new[] object {"foo", "bar"}));
// Another way to do it, more strongly-typed
var foo = A.Fake<FooClass>(x =>
x.WithArgumentsForConstructor(
()=> new FooClass("foo", "bar")));
Well, if you think about that, there’s not a simple way to abstract a non-generic, non-typed expression with a generic, strong-typed, functional expression, well, not a simple way to do it at least you use the expressiveness of binary expression trees. How do we abstract this? well, let’s see the simple expression:
Now, let’s create a function to return that expression based in parameters, let’s abstract everything with the only knowlege of the type and the parameters:
// Transforming this:
var foo = A.Fake<T>(Action<IFakeOptionsBuilder<T>> opts)
// Implies something like this
public static Delegate CreateForType(Type type, object[] args) {
var optType = typeof(IFakeOptionsBuilder<>).MakeGenericType(new[] { type });
var actType = typeof(Action<>).MakeGenericType(new[] { optType });
// Now we have the type for the action and the options builder
// Let's use it to create an expression tree!
var r = Expression.Parameter(optType, "r");
var method = optType.GetMethod("WithArgumentsForConstructor", new[] { typeof(IEnumerable<object>) });
var p = Expression.Constant(ctorArgs);
var call = Expression.Call(r, method, p);
var lambda = Expression.Lambda(actType, call, new[] { r });
var exp = lambda.Compile();
}
And well, now that we have the function to call (we generated it), it’s time to create the fake
public object CreateFake(Type type, params object[] args) {
var closedFakeType = typeof(Fake<>).MakeGenericType(interfaceType);
var objectProperty = closedFakeType.GetProperty("FakedObject", interfaceType);
var options = args != null && args.Length > 0
? FakeItEasyHelper.CreateForType(interfaceType, args) : null;
var instance = args != null && args.Length > 0
? Activator.CreateInstance(closedFakeType, new object[] {options})
: Activator.CreateInstance(closedFakeType);
return objectProperty.GetValue(instance, null);
}
See? that’s the reason because Functional programming it’s so awesome, functions are just another variable, you can generate them, use them, modify them, use them. Next time you have a problem try to think in a functional way, you will see how awesome is!.
More about Expression Trees in the amazing book C# in Depth, Second Edition by John Skeet or go and pick a functional language (like this Programming F#
), study it, enjoy it, use it in your favourite non-functional language.
Happy coding!
NOTE: the patch it’s already included in mfakes, no worries!
