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.
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()) });
}
}