PROBLEM TO SOLVE
I'm trying to create a mock of interface ISomeService
using Moq library which will wrap its real implementation SomeService
.
Right now I'm doing this manually like this:
var instance = new SomeService();
var mock = new Mock<ISomeSorvice>();
mock.Setup(x => x.Run(It.IsAny<int>()))
.Returns(x => instance.Run(x));
It calls real implementation thus my tests are more reliable and I get benefits of a mock like verify etc. Problem is that I have to implement it for every method and every service I use which is not the way to go. I'm aiming for fully automated solution.
WHAT I DID
What I have for Setup()
part is
// (x)
var mockParameter = Expression.Parameter(typeof(T), "x");
// It.IsAny<T>()
var isAnyMethod = typeof(It).GetMethod(nameof(It.IsAny));
var isAny = parameters.Select(x => Expression.Call(isAnyMethod.MakeGenericMethod(x.ParameterType))).ToArray();
// x.Run(It.IsAny<T>())
mockCall = Expression.Call(mockParameter, method, isAny);
And now the fun part. If I define lambda like this:
// (x) => x.Run(It.IsAny<T>())
var mockLambda = Expression.Lambda(mockCall, mockParameter);
It produces a LambdaExpression
and it doesn't work with the Setup
method definition which requires fully defined expression:
public ISetup<T, TResult> Setup<TResult>(Expression<Func<T, TResult>> expression)
I can't call Setup
directly because I don't know the type of TResult
obviously thus I'm resorting to reflection:
var setupMethod = typeof(Mock<>)
.GetMethods()
.Where(x => x.Name == nameof(mock.Setup) && x.GetParameters()[0].ParameterType.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(Func<,>)).First()
.MakeGenericMethod(method.ReturnType);
var setupResult = setupMethod.Invoke(mock, new object[] { mockLambda });
but I get an error
System.InvalidOperationException: 'Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.'
which makes sense but is disappointing.
WHAT I TRIED
I tried to cast result type to object
type
var converted = Expression.Convert(mockCall, typeof(object));
so I could write
// (x) => (object)x.Run(It.IsAny<T>())
var mockLambda = Expression.Lambda<Func<T, object>>(converted, mockParameter);
but this also throws an exception
System.ArgumentException: 'Unsupported expression: (object)x.Run(It.IsAny())'
To make this work I added a bunch of else if statements for value types where for given result type I return specific Expression<Func<T, ResultType>>
but this is a nightmare to maintain and extend. For classes I use object
and at least that part works.
For value types that are user defined I added generic method
void SetupUnsuportedMethod<TMethodResult>(MethodInfo methodInfo)
and user has to add missing Setup
himself.
QUESTION
Is there a way to make this code fully generic? Maybe there is a better way to solve this problem? Or do I have to stick to this nasty else if solution?
I think you can make good use of DispatchProxy
for this.
void Main()
{
var impl = new A();
var mock = new Mock<I>();
var proxy = DispatchProxyOf<I>.CreateProxy(mock.Object, impl);
// actually pass the proxy to SUTs and depedencies
// here a local method to simulate this
DependentOnI(proxy);
void DependentOnI(I service)
{
Console.WriteLine(service.Foo());
}
mock.Verify(x => x.Foo(), Times.Once); // passes
}
class A : I
{
public int Foo() => 42;
}
public interface I
{
int Foo();
}
class DispatchProxyOf<T> : DispatchProxy
{
private T Mock { get; set; }
private T Implementation { get; set; }
public static T CreateProxy(T mock, T implementation)
{
var proxyInterfaceReference = DispatchProxy.Create
<T, DispatchProxyOf<T>>();
var proxy = proxyInterfaceReference as DispatchProxyOf<T>;
proxy.Mock = mock;
proxy.Implementation = implementation;
return proxyInterfaceReference;
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
// for Mock.Verify
targetMethod.Invoke(Mock, args);
// for Mock.Setup
return targetMethod.Invoke(Implementation, args);
}
}