2016-01-17 2 views
2

Пожалуйста, обратите внимание на следующий код:Почему поведение Matching.ImplementedInterfaces отличается от Matching.ExactType и FrozenAttribute.As?

public class TestingSample 
{ 
    public class FactoryClass : Class {} 

    public class Class : IInterface {} 

    public interface IInterface {} 

    public class AutoData : AutoDataAttribute 
    { 
     public AutoData() : base(Create()) {} 

     static IFixture Create() 
     { 
      var fixture = new Fixture(); 
      fixture.Customize<IInterface>(composer => composer.FromFactory(() => new FactoryClass())); 
      fixture.Customize<Class>(composer => composer.FromFactory(() => new FactoryClass())); 
      return fixture; 
     } 
    } 

    [Theory, TestingSample.AutoData] 
    public void OldSkool([Frozen(As = typeof(IInterface))]Class first, Class second, IInterface third) 
    { 
     Assert.IsType<FactoryClass>(first); 
     Assert.Same(first, second); 
     Assert.Same(first, third); 
    } 

    [Theory, TestingSample.AutoData] 
    public void DirectBaseType([Frozen(Matching.ExactType)]Class first, Class second) 
    { 
     Assert.IsType<FactoryClass>(first); 
     Assert.Same(first, second); 
    } 

    [Theory, TestingSample.AutoData] 
    public void ImplementedInterfaces([Frozen(Matching.ImplementedInterfaces)]Class first, IInterface second) 
    { 
     Assert.IsType<FactoryClass>(first); 
     Assert.Same(first, second); // The Fails. 
    } 
} 

Как вы можете (я надеюсь) увидеть, тест ImplementedInterfaces терпит неудачу. Поскольку FrozenAttribute.As устарел, и пользователи были перенаправлены на перечисление совпадений, я ожидал, что он будет вести себя так же, как и раньше.

Однако, похоже, что Match.ImplementedInterfaces ведет себя иначе как от Match.ExactType, так и от FrozenAttribute.As.

я сделал некоторые спелеология и увидел, что Match.ExactType и FrozenAttribute.As воспользоваться SeedRequestSpecification тогда Match.ImplementedInterfaces соответствует только на Type запросов.

Возможно ли получить некоторый контекст вокруг этого поведения? Это по дизайну? И если да, существует ли известная рекомендация по дизайну таким образом, чтобы восстановить старое поведение, используя Match.ImplementedInterfaces?

ответ

4

Во-первых, оговорка: код, указанный в OP, не ведет вполне, как описано на моей машине, используя AutoFixture 3.39.0. Разница заключается в том, что первое утверждение в этом тесте проходит:

[Theory, TestingSample.AutoData] 
public void ImplementedInterfaces(
    [Frozen(Matching.ImplementedInterfaces)]Class first, 
    IInterface second) 
{ 
    Assert.IsType<FactoryClass>(first); // passes 
    Assert.Same(first, second); // fails 
} 

Тем не менее, я допускаю, что это (немного) удивительно, что второе утверждение не удается.

Краткое пояснение состоит в том, что при текущей реализации замораживание выполняется во время отражения, а не во время выполнения. Когда AutoFixture.Xunit2 определяет, что замерзать, он смотрит на тип параметра, на который применяется атрибут [Frozen]. Это Class, а не FactoryClass, поэтому результат заключается в том, что FactoryClass не заморожен вообще!

Вы можете увидеть это из этого теста:

[Theory, TestingSample.AutoData] 
public void FactoryClassIsNotFrozen(
    [Frozen(Matching.ImplementedInterfaces)]Class first, 
    FactoryClass second) 
{ 
    Assert.IsType<FactoryClass>(first); // passes 
    Assert.IsType<FactoryClass>(second); // passes 
    Assert.Same(first, second); // fails 
} 

Является ли это наилучшим возможным осуществление? Возможно, нет, но так оно и работает. Существует an open issue in the AutoFixture GitHub repository, в котором предполагается, что реализация замораживания должна быть реорганизована, чтобы работать больше, чем время жизни синглтона DI контейнера. Это может изменить поведение в этом конкретном сценарии на то, что ему подходит. Будут ли у него какие-то недостатки, я не могу сейчас сказать.

Когда мы переработаны [Frozen] атрибута использовать более гибкие правила Matching, мне было известно, что новая система не сможет быть заменой 100% для старой As собственности. Я все еще думаю, что компромисс стоит того.

Хотя As позволяет получить эту конкретную функцию для работы, это потому, что вы, как программист, знаете что Class орудия IInterface, и поэтому [Frozen(As = typeof(IInterface))] аннотацию имеют смысл.

Вы можете утверждать, что As более гибкий, но в основном потому, что он не имеет встроенного интеллекта.Вы также могли бы написать [Frozen(As = typeof(IAsyncResult))], и это было бы скомпилировано просто отлично - только для сбоев во время выполнения, потому что это полная бессмыслица.

Есть ли известная рекомендация по дизайну таким образом, чтобы восстановить старое поведение с использованием Match.ImplementedInterfaces?

Да, рассмотрите упрощение конструкции системного теста (SUT).

AutoFixture первоначально был задуман как инструмент для разработки тестов, и это все еще является его основной целью. В духе GOOS, мы должны прослушать тесты. Если тест трудно записать, первой реакцией должно быть упрощение SUT. AutoFixture имеет тенденцию усиливать такую ​​обратную связь от тестов.

Вам действительно нужно сопоставить то, что реализует интерфейс и происходит от базового класса? Зачем?

Могло ли быть сделано проще?

+0

Фантастический, всесторонний ответ (как всегда!) @ Mark-seemann. Очень признателен. Вы действительно правильно относитесь к неудачному тесту оговорки/неправильно (отмеченному). Это было ошибкой в ​​том, как я читал результаты (без фактического нажатия строки для проверки). Чтобы понять суть проблемы, это, скорее всего, потому, что я все еще изучаю TDD и тестирование в целом, но, как и насмешка, мне нравится идея использовать фабрику для создания моего SUT (или поддерживающих объектов) и использовать его внутри тест. В этом суть проблемы здесь: создать элемент с фабрики и заморозить его (как одноэлементный). –

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