2017-02-20 28 views
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 для нашего МОК и обнаружил, мы можем получить большую прибыль производительности (под нагрузкой), используя Func<T> подход, потому что эти зависимости не разрешатся, пока они не будут и в некоторых сценариях некоторые зависимости не используются.Moq CreateInstance терпит неудачу, когда конструктор имеет зависимости с помощью 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'

Edit 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

Правки имеют смысл, как вы действительно должны настройте делегата (Func), а также то, что вы хотите высмеять. Вероятно, вы могли бы написать метод расширения для 'AutoMocker', например' SetupFunc (Mock mock, object returnData) ', который содержит код шаблона, который вы должны были записать в Edit 2. – Scott

ответ

2

Вы можете автоматически соединять с Func<T> для каждого T с AutoMocker делать что-то вроде этого:

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

Вы пытались использовать Lazy<T> вместо Func<T>, чтобы достичь желаемой ленивой загрузки? Это может играть лучше с Moq, чем Func.

Documentation on Lazy

+0

Согласно моему последнему праву, у меня все работает , однако, это немного раздражает, нужно установить несколько вещей. Я попробовал «Lazy » и считаю, что это приводит к той же проблеме. Однако я буду исследовать 'Lazy ' vs 'Func ' с точки зрения производительности. – acarter

+0

Да, если ничего другого, я бы рекомендовал переключиться на 'Lazy ' в любом случае, так что цель кода понятна. – Scott

Смежные вопросы