1
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- 如果只需要做一个就好了,否则每次测试都会有更多的开销。

4

2 回答 2

2

您可以使用 AutoMocker自动连接 a Func<T>for each ,执行如下操作: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>,您必须Lazy<T>使用 a实例化Func<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()) });
    }
}
于 2017-02-21T00:38:34.947 回答
0

您是否尝试过使用Lazy<T>而不是Func<T>实现您想要的延迟加载?与 Moq 相比,它可能比 Func 玩得更好。

懒惰的文档

于 2017-02-20T23:37:58.880 回答