2014-02-04 2 views
8

я определил ISpecimenBuilder для моих моделей и использовать его так:Как я могу добавить общий постобработки применяется после настройки

new Fixture().Customize(new ModelCustomization()); 

Я хочу, чтобы использовать его в большинстве моих тестов относительно модели. Я также хочу применить некоторую форму пост-обработки в одном из моих тестовых классов. В частности, я хочу заполнить свойство CompanyHistory всего созданного Offers. Такое ощущение, что это можно сделать так:

fixture.Build<Offer>() 
.With(o => o.CompanyHistory, _previouslyCreatedCompanyHistory) 
.Create(); 

Но Build<T> отключает все настройки и мне нужны.

Могу ли я сделать что-то подобное?

fixture.Build<Offer>() 
.WithCustomization(new ModelCustomization()) // there is no such method, but i'd like it to be 
.With(o => o.CompanyHistory, _previouslyCreatedCompanyHistory) 
.Create(); 

Или я должен написать свое собственное поведение? Если да, может ли кто-нибудь предоставить мне рекомендации по этому поводу?

EDIT: Я чувствую, что я должен подчеркнуть, что я хочу использовать обе мои общие настройки (ModelCustomization) и постпроцессор

EDIT 2: Что я имел в виду с самого начала является то, что ModelCustomization может (и должен) создать Offer, и мой будущий постпроцессор должен использовать этот уже созданный образец и заполнить некоторые его свойства.

ответ

0

В итоге я написал следующие настройки:

private class OfferWithCompanyModelCustomization: ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customizations.Add(new FilteringSpecimenBuilder(new Postprocessor(
      new ModelSpecimenBuilder(), new FillModelPropertiesCommand()), new ExactTypeSpecification(typeof(Offer)))); 
    } 

    private class FillModelPropertiesCommand : ISpecimenCommand 
    { 
     public void Execute(object specimen, ISpecimenContext context) 
     { 
      var offer = specimen as Offer; 
      offer.CompanyHistory = (CompanyHistory)context.Resolve(typeof(CompanyHistory)); 
     } 
    } 
} 

Это работает, но это далеко не идеально. Как вы можете видеть, я напрямую ссылаюсь на ModelSpecimenBuilder, поэтому я зависим от реализации (как постпроцессор, которого я бы не хотел).

Ответа на этот вопрос @Nikos не удовлетворив, так как его настройка игнорирует предыдущие настройки в цепочке ответственности.

Когда мы вызываем метод Create, CompositeSpecimenBuilder будет ссылаться на метод Create всех его содержащихся в нем строителей, пока один из них не предоставит образец. На этом этапе запрос считается выполненным, а остальные строители игнорируются.

источник: AutoFixture Documentation

5

Вот как вы можете создать и использовать Postprocessor в этом случае:

[Fact] 
public void Test() 
{ 
    var fixture = new Fixture(); 

    // (You may also include other customizations here.) 

    fixture.Customizations.Add(
     new FilteringSpecimenBuilder(
      new Postprocessor(
       new MethodInvoker(
        new ModestConstructorQuery()), 
       new OfferFiller()), 
      new OfferSpecification())); 

    var offer = fixture.Create<Offer>(); 
    // -> offer.CompanyHistory has the value supplied in OfferFiller command. 
} 

Команда OfferFiller определяется как:

internal class OfferFiller : ISpecimenCommand 
{ 
    public void Execute(object specimen, ISpecimenContext context) 
    { 
     if (specimen == null) 
      throw new ArgumentNullException("specimen"); 
     if (context == null) 
      throw new ArgumentNullException("context"); 

     var offer = specimen as Offer; 
     if (offer == null) 
      throw new ArgumentException(
       "The specimen must be an instance of Offer.", 
       "specimen"); 

     Array.ForEach(offer.GetType().GetProperties(), x => 
     { 
      if (x.Name == "CompanyHistory ") 
       x.SetValue(offer, /*value*/); 
      else 
       x.SetValue(offer, context.Resolve(x.PropertyType)); 
     }); 
    } 
} 

OfferSpecification определяется как:

internal class OfferSpecification : IRequestSpecification 
{ 
    public bool IsSatisfiedBy(object request) 
    { 
     var requestType = request as Type; 
     if (requestType == null) 
      return false; 

     return typeof(Offer).IsAssignableFrom(requestType); 
    } 
} 
+0

Спасибо за ответ! Проблема в том, что я хочу одновременно использовать как мою общую настройку (ModelCustomization, используемую во всех тестах), так и мой менее распространенный постпроцессор (OfferFiller, используемый в нескольких тестах) (проверьте мое 3-е поле кода). Я решил, что я могу создать FilteringSpecimenBuilder с оригинальным ModelSpecimenBuilder и custom OfferFiller как Command, но я чувствую, что он недостаточно прочен – joozek

+0

Вы также можете это сделать; включая любые настройки после создания нового экземпляра 'Fixture' (см. обновленный комментарий кода). –

+0

Да, но я думаю, что только одна настройка позволяет создать образец, и я хочу, чтобы он был создан ModelCustomization, а позже обработан после обработки – joozek

1

У меня была аналогичная проблема, и попытались решения, упомянутые здесь, но они не работают, как ожидалось.Наконец, я нашел реализацию PostProcessWhereIsACustomization класса, что делает именно то, что мне нужно:

AutoFixture настройки, чтобы позволить вставку произвольной логики постобработки а-ля Customize (с => c.Do()), но в глобальный вариант Пересмотрено для v3 (начально для v2)

Может спасти кого-нибудь в Google.

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