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

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

私たちは IOC に Autofac を使用しており、この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これは機能するようになりました。

ただし、解決すべき問題がもう 1 つあります。

_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必要があります。1 つだけを実行する必要がある場合は便利ですが、そうでない場合は、テストごとのオーバーヘッドが大きくなります。

4

2 に答える 2

2

次のようなことを行う AutoMockerでFunc<T>for everyを自動的に接続できます。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>必要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 に答える