1
public class MyService
{
    private readonly ISomething _something;
    private readonly Func<IRarelyGetUsed> _rarelyGetUsed;

    public MyService(ISomething something, Func<IRarelyGetUsed> rarelyGetUsed)
    {
        _something = something;
        _rarelyGetUsed = rarelyGetUsed;
    }
}

We use Autofac for our IOC and found we can get big performance gains (when under load) using the Func<T> approach because those dependencies don't get resolved until they are used, and in some scenarios certain dependencies are not used.

We are also using Moq for some unit testing.

var _container = new AutoMocker();
var _service = _container.CreateInstance<MyService>();

At this point it blows up - System.NullReferenceException : Object reference not set to an instance of an object.

Anyone know how to tell Moq to play nicely with Func dependencies?

Note that if I change Func<IRarelyGetUsed> to IRarelyGetUsed there's no exception.

Edit: Turns out the nuget package was pretty old - after updating the package https://github.com/tkellogg/Moq.AutoMocker this is now working.

However, there's one more problem to solve -

_container.GetMock<Func<IRarelyGetUsed>>().Setup(p => p().DoSomething(It.IsAny<string>())).Returns(true).Verifiable();

Trying to setup the result of the above method result in - Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.InvocationExpression'

Edit 2:

var serviceMock = _container.GetMock<IRarelyGetUsed>();
serviceMock.Setup(r => r.DoSomething()).Returns(someData);
_container.GetMock<Func<IRarelyGetUsed>>().Setup(s => s()).Returns(serviceMock.Object);

The above now works, however it requires setting up both the Func<IRarelyGetUsed> and IRarelyGetUsed - would be nice if it was only necessary to do one, otherwise there's more overhead per test.

Bennor McCarthy
  • 11,415
  • 1
  • 49
  • 51
acarter
  • 55
  • 10
  • 1
    Your edits make sense, as you indeed have to set up the delegate (the Func) as well as the actual thing you want mocked. You could probably write an extension method for `AutoMocker` like `SetupFunc(Mock mock, object returnData)` that does the boilerplate code you had to write up in Edit 2. – Doctor Blue Feb 21 '17 at 00:30

2 Answers2

2

You can automatically wire up a Func<T> for every T with AutoMocker doing something like this:

public void RegisterFuncs(AutoMocker autoMocker, IEnumerable<Type> types)
{
    var use = typeof(AutoMocker).GetMethods()
        .First(t => t.Name == "Use" && 
                    t.GetGenericArguments().First().Name == "TService");
    var get = typeof(AutoMocker).GetMethod("Get");
    foreach (var type in types)
    {
        // _.container.Use<Func<T>>()
        var typedUse = use.MakeGenericMethod(typeof(Func<>).MakeGenericType(type));

        // _container.Get<T>()
        var typedGet = get.MakeGenericMethod(type);
        var target = Expression.Constant(autoMocker);
        var call = Expression.Call(target, typedGet);

        // () => _container.Get<T>()
        var lambda = Expression.Lambda(call);

        // _.container.Use<Func<T>>(() => _container.Get<T>())
        typedUse.Invoke(autoMocker, new object[] { lambda.Compile() });
    }
}

// Then call with your AutoMocker instance and the interfaces you want to wire up
var types = typeof(SomeNamespace.ISomeInterface).Assembly.GetExportedTypes()
    .Where(t => t.IsInterface && !t.ContainsGenericParameters);
RegisterFuncs(yourAutoMocker, types);

Run this in your test setup just after creating a container.

Note: to make the above work for Lazy<T>, you have to instantiate the Lazy<T> with a Func<T>, so you'll need something like the following:

public void RegisterLazys(AutoMocker autoMocker, IEnumerable<Type> types)
{
    var use = typeof(AutoMocker).GetMethods()
        .First(t => t.Name == "Use" && 
                    t.GetGenericArguments().First().Name == "TService");
    var get = typeof(AutoMocker).GetMethod("Get");
    foreach (var type in types)
    {
        // Lazy<T>
        var lazyT = typeof(Lazy<>).MakeGenericType(type);

        // _.container.Use<Lazy<T>>()
        var typedUse = use.MakeGenericMethod(lazyT);

        // _container.Get<T>()
        var typedGet = get.MakeGenericMethod(type);
        var target = Expression.Constant(autoMocker);
        var call = Expression.Call(target, typedGet);

        // () => _container.Get<T>()
        var lambda = Expression.Lambda(call);

        // _.container.Use<Lazy<T>>(new Lazy<T>(() => _container.Get<T>()));
        typedUse.Invoke(autoMocker, new object[] { Activator.CreateInstance(lazyT, lambda.Compile()) });
    }
}
Bennor McCarthy
  • 11,415
  • 1
  • 49
  • 51
0

Have you tried using Lazy<T> instead of Func<T> to achieve the lazy loading you desire? It may play better with Moq than Func does.

Documentation on Lazy

Doctor Blue
  • 3,769
  • 6
  • 40
  • 63
  • As per my latest edit I've got it all working, however, it's just a bit annoying needing to set multiple things up. I tried `Lazy` and believe it results in the same issue. However I will be investigating `Lazy` vs `Func` from a performance point of view. – acarter Feb 21 '17 at 00:11
  • Yeah if nothing else I'd recommend switching to `Lazy` anyway, so that the intent of the code is clearer. – Doctor Blue Feb 21 '17 at 00:28