当构造函数具有使用 Func<T> 的依赖项时,Moq CreateInstance 失败
Moq CreateInstance fails when constructor has dependencies using Func<T>
public class MyService
{
private readonly ISomething _something;
private readonly Func<IRarelyGetUsed> _rarelyGetUsed;
public MyService(ISomething something, Func<IRarelyGetUsed> rarelyGetUsed)
{
_something = something;
_rarelyGetUsed = rarelyGetUsed;
}
}
我们将 Autofac 用于 IOC 并发现使用 Func<T>
方法可以获得巨大的性能提升(在负载下时),因为这些依赖关系在使用之前不会得到解决,并且在某些情况下某些情况下不使用依赖项。
我们也使用 Moq 进行一些单元测试。
var _container = new AutoMocker();
var _service = _container.CreateInstance<MyService>();
此时爆炸了-System.NullReferenceException : Object reference not set to an instance of an object.
有人知道如何告诉 Moq 与 Func 依赖项一起玩吗?
请注意,如果我将 Func<IRarelyGetUsed>
更改为 IRarelyGetUsed
,也不例外。
编辑: 原来 nuget 包很旧 - 更新包后 https://github.com/tkellogg/Moq.AutoMocker
现在可以使用了。
但是,还有一个问题需要解决 -
_container.GetMock<Func<IRarelyGetUsed>>().Setup(p => p().DoSomething(It.IsAny<string>())).Returns(true).Verifiable();
尝试设置上述方法的结果 - Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.InvocationExpression'
编辑 2:
var serviceMock = _container.GetMock<IRarelyGetUsed>();
serviceMock.Setup(r => r.DoSomething()).Returns(someData);
_container.GetMock<Func<IRarelyGetUsed>>().Setup(s => s()).Returns(serviceMock.Object);
上面的现在可以工作了,但是它需要设置 Func<IRarelyGetUsed>
和 IRarelyGetUsed
- 如果只需要做一个就好了,否则每次测试的开销会更多。
您是否尝试过使用 Lazy<T>
而不是 Func<T>
来实现您想要的延迟加载?与 Moq 一起使用可能比 Func 更好。
您可以使用 AutoMocker 为每个 T
自动连接一个 Func<T>
:
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);
运行 在创建容器后的测试设置中。
注意:要使上述工作适用于 Lazy<T>
,您必须使用 Func<T>
实例化 Lazy<T>
,因此您需要如下内容:
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()) });
}
}
public class MyService
{
private readonly ISomething _something;
private readonly Func<IRarelyGetUsed> _rarelyGetUsed;
public MyService(ISomething something, Func<IRarelyGetUsed> rarelyGetUsed)
{
_something = something;
_rarelyGetUsed = rarelyGetUsed;
}
}
我们将 Autofac 用于 IOC 并发现使用 Func<T>
方法可以获得巨大的性能提升(在负载下时),因为这些依赖关系在使用之前不会得到解决,并且在某些情况下某些情况下不使用依赖项。
我们也使用 Moq 进行一些单元测试。
var _container = new AutoMocker();
var _service = _container.CreateInstance<MyService>();
此时爆炸了-System.NullReferenceException : Object reference not set to an instance of an object.
有人知道如何告诉 Moq 与 Func 依赖项一起玩吗?
请注意,如果我将 Func<IRarelyGetUsed>
更改为 IRarelyGetUsed
,也不例外。
编辑: 原来 nuget 包很旧 - 更新包后 https://github.com/tkellogg/Moq.AutoMocker
现在可以使用了。
但是,还有一个问题需要解决 -
_container.GetMock<Func<IRarelyGetUsed>>().Setup(p => p().DoSomething(It.IsAny<string>())).Returns(true).Verifiable();
尝试设置上述方法的结果 - Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.InvocationExpression'
编辑 2:
var serviceMock = _container.GetMock<IRarelyGetUsed>();
serviceMock.Setup(r => r.DoSomething()).Returns(someData);
_container.GetMock<Func<IRarelyGetUsed>>().Setup(s => s()).Returns(serviceMock.Object);
上面的现在可以工作了,但是它需要设置 Func<IRarelyGetUsed>
和 IRarelyGetUsed
- 如果只需要做一个就好了,否则每次测试的开销会更多。
您是否尝试过使用 Lazy<T>
而不是 Func<T>
来实现您想要的延迟加载?与 Moq 一起使用可能比 Func 更好。
您可以使用 AutoMocker 为每个 T
自动连接一个 Func<T>
:
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);
运行 在创建容器后的测试设置中。
注意:要使上述工作适用于 Lazy<T>
,您必须使用 Func<T>
实例化 Lazy<T>
,因此您需要如下内容:
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()) });
}
}