2012-04-10 2 views
7

У меня есть объектную модель, которая использует Open Generics (Да, да, теперь у меня есть две проблемы, вот почему я здесь :): -AutoFixture: Настройка Open Дженерики ОБРАЗЦОВ Builder

public interface IOGF<T> 
{ 
} 

class C 
{ 
} 

class D 
{ 
    readonly IOGF<C> _ogf; 

    public D(IOGF<C> ogf) 
    { 
     _ogf = ogf; 
    } 
} 

Я m пытается получить AutoFixture для генерации анонимных экземпляров D выше. Тем не менее, сама по себе, AutoFixture не имеет встроенной в стратегии построения IOGF<> и, следовательно, мы наблюдаем:

public class OpenGenericsBinderDemo 
{ 
    [Fact] 
    public void X() 
    { 
     var fixture = new Fixture(); 

     Assert.Throws<Ploeh.AutoFixture.ObjectCreationException>(() => 
      fixture.CreateAnonymous<D>()); 
    } 

Основополагающее сообщение:

Ploeh.AutoFixture.ObjectCreationException: AutoFixture был не удалось создать экземпляр из IOGF`1 [C], скорее всего, потому, что он не имеет открытого конструктора, является абстрактным или непубличным.

Я рад предоставить ему конкретную реализацию:

public class OGF<T> : IOGF<T> 
{ 
    public OGF(IX x) 
    { 
    } 
} 

public interface IX 
{ 
} 

public class X : IX 
{ 
} 

и связанный с обязательными:

fixture.Register<IX,X>(); 

Как (или я должен даже взглянуть на проблему, что путь ??) сделать следующий тестовый проход?

public class OpenGenericsLearning 
{ 
    [Fact] 
    public void OpenGenericsDontGetResolved() 
    { 
     var fixture = new Fixture(); 
     fixture.Inject<IX>(fixture.Freeze<X>()); 

     // TODO register or do something that will provide 
     //  OGF<C> to fulfill D's IOGF<C> requirement 

     Assert.NotNull(fixture.CreateAnonymous<D>()); 
    } 
} 

(Есть обсуждения и вопросы, связанные с этим на сайте CodePlex - я просто нужно быстро осущ этого и я открыт для удаления этого, если это просто плохая идея, и/или я что-то пропустил)

РЕДАКТИРОВАТЬ 2: (См. Также комментарий к ответу Марка). Контекст (по общему признанию, придуманный) здесь является приемочным тестом на большом «почти полном» системном графике объекта «Система под тестом», а не на небольшом (контролируемом/:) пара или триплет классов в единичном или интеграционном тестовом сценарии. Как упоминалось в заявлении о скором вопросе, я не совсем уверен, что этот тип теста даже имеет смысл.

+2

AutoMoq, AutoRhinoMocks и AutoFakeItEasy являются расширениями, которые позволяют использовать AutoFixture как авто-насмешливый контейнера. Это вариант? (Потому что таким образом вы можете успешно создать анонимный экземпляр D.) –

+0

@Nikos Знает расширения автомодели и мысленно исключил их - shoud упомянули об этом; см. мои комментарии к отвечу Марка для получения дополнительной информации). Принимая этот комментарий вместе с ответом Марка на сердце, я буду удваивать свои усилия, чтобы не полагаться на мой костыль! –

+0

related: http://stackoverflow.com/questions/20209469/how-can-i-register-a-generic-object-factory –

ответ

7

Вы можете создать настройки, которая работает следующим образом:

public class AnOpenGenericsBinderDemo 
{ 
    [Fact] 
    public void RegisteringAGenericBinderShouldEnableResolution() 
    { 
     var fixture = new Fixture(); 
     fixture.Inject<IX>(fixture.Freeze<X>()); 
     fixture.RegisterOpenGenericImplementation(typeof(IOGF<>), typeof(OGF<>)); 

     Assert.IsType<OGF<C>>(fixture.CreateAnonymous<D>().Ogf); 
    } 
} 

И реализуется следующим образом:

public static class AutoFixtureOpenGenericsExtensions 
{ 
    public static void RegisterOpenGenericImplementation(this IFixture that, Type serviceType, Type componentType) 
    { 
     if (!serviceType.ContainsGenericParameters) 
      throw new ArgumentException("must be open generic", "serviceType"); 
     if (!componentType.ContainsGenericParameters) 
      throw new ArgumentException("must be open generic", "componentType"); 
     // TODO verify number of type parameters is 1 in each case 
     that.Customize(new OpenGenericsBinderCustomization(serviceType, componentType)); 
    } 

    public class OpenGenericsBinderCustomization : ICustomization 
    { 
     readonly Type _serviceType; 
     readonly Type _componentType; 

     public OpenGenericsBinderCustomization(Type serviceType, Type componentType) 
     { 
      _serviceType = serviceType; 
      _componentType = componentType; 
     } 

     void ICustomization.Customize(IFixture fixture) 
     { 
      fixture.Customizations.Add(new OpenGenericsSpecimenBuilder(_serviceType, _componentType)); 
     } 

     class OpenGenericsSpecimenBuilder : ISpecimenBuilder 
     { 
      readonly Type _serviceType; 
      readonly Type _componentType; 

      public OpenGenericsSpecimenBuilder(Type serviceType, Type componentType) 
      { 
       _serviceType = serviceType; 
       _componentType = componentType; 
      } 

      object ISpecimenBuilder.Create(object request, ISpecimenContext context) 
      { 
       var typedRequest = request as Type; 
       if (typedRequest != null && typedRequest.IsGenericType && typedRequest.GetGenericTypeDefinition() == _serviceType) 
        return context.Resolve(_componentType.MakeGenericType(typedRequest.GetGenericArguments().Single())); 
       return new NoSpecimen(request); 
      } 
     } 
    } 
} 

Я предполагаю, что кто-то имеет более эффективной реализации, чем что, хотя и/или есть встроенная реализация.

EDIT: Ниже обновленный D со свойством восприятия:

class D 
{ 
    readonly IOGF<C> _ogf; 

    public D(IOGF<C> ogf) 
    { 
     _ogf = ogf; 
    } 

    public IOGF<C> Ogf 
    { 
     get { return _ogf; } 
    } 
} 
+0

У меня есть исключение: System.ArgumentException: Объект типа 'Ploeh.AutoFixture.Kernel. OmitSpecimen 'не может быть преобразован в тип SelogerCity.Data.Entities.User. с вышеуказанной настройкой. Тип был бы ISet или IList ... способ много работать, чтобы сделать эту библиотеку полезной. – Sam

+0

@ Прошу прощения, не видел этого до сих пор. У вас есть неудачный тест? (Я закончил использовать этот код, и он на сегодняшний день удовлетворил мои потребности, но я не знаю, что вы делаете, и, следовательно, не может действительно начинать угадывать, какое отлитие или сравнение в моем коде нуждается в улучшении) –

+2

Фантастический, работает как ожидаемый для 'IObservable <> -> Subject <>' в библиотеке Rx – AlexFoxGill

4

AFICT нет открытых дженериков в поле зрения. D полагается на IOGF<C>, который является построенным типом.

Сообщение об ошибке не из-за открытых дженериков, а потому, что IOGF<C> - это интерфейс.

Вы можете поставить a mapping от IOGF<C> к OGF<C> так:

fixture.Register<IOGF<C>>(() => fixture.CreateAnonymous<OGF<C>>()); 

С OGF<C> полагается на IX вам также необходимо предоставить отображение в X:

fixture.Register<IX>(() => fixture.CreateAnonymous<X>()); 

Это должно сделать трюк.

Однако, как отмечает Никос Баксеванис в своем комментарии, если вы используете один из трех прилагаемых автомозаических расширений, это будет в основном работать из коробки - например,

var fixture = new Fixture().Customize(new AutoMoqCustomization()); 
var d = fixture.CreateAnonymous<D>(); 
+0

+1 Спасибо, Марк. Я думаю, вопрос немного ухищрен, и я не придавал достаточной энергии, чтобы сделать его правдоподобным. То, во что я ехал, было желание иметь возможность сопоставлять открытые общие возможности привязки рамки DI (и расширения условных обозначений). Это происходит из-за того, что мы/мы [ab] используем AF много в тестах приемки и интеграции, в которых светит способность создавать все виды запутанных контекстных классов и помощников (но также является скользким наклоном к сложным сложностям) , Ваша точка зрения хорошо сделана, что не должно быть необходимости в такой привязке в любых разумных модульных тестах. –

+0

(Я знаю большую часть материала, который вы указали, и соглашаетесь с дизайнерским решением не предлагать перегрузки (например, «Register»), которые берут тип «Type», который потенциально может сделать это опрятным. фактическая проблема будет решена путем перепроектирования тестовых контекстных классов/приспособлений, для которых это желание существует. –

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

  • Нет связанных вопросов^_^