Autofixture: A construção de uma instância personalizada falha em referências circulares

Criado em 13 jan. 2018  ·  5Comentários  ·  Fonte: AutoFixture/AutoFixture

    [TestFixture]
    public class TestAutoFixture4
    {
        [Test]
        [AutoMoqData]
        public void CreatingAnDummyObjectShouldNotThrow(IFixture fixture)
        {
            fixture.Invoking(x => x.Create<DummyObject>()).ShouldNotThrow();
        }

        [Test]
        [AutoMoqData]
        public void BuildingAnDummyObjectShouldNotThrow(IFixture fixture)
        {
            fixture.Invoking(x => x.Build<DummyObject>().Create()).ShouldNotThrow();
        }
    }

    public class DummyObject
    {
        public Guid Id { get; set; }
        public DummyObject CircularRef { get; set; }
    }

    public class AutoMoqDataAttribute : AutoDataAttribute
    {
        public AutoMoqDataAttribute()
            : base(new Fixture()
                 .Customize(
                    new DummyCustomization()
                ))
        { }
    }

    public class DummyCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customize<DummyObject>(c =>
                c.Without(x => x.CircularRef)
            );
        }
    }

Acabei de atualizar o AutoFixture de 3.50.6 para 4.0.0.

O código acima funciona perfeitamente no 3.50.6, mas apenas o primeiro teste (usando fixture.Create) passa no 4.0.0 (.NET 4.6.2, NUnit3.7.1, FluentAssertions 4.19.4).

O teste usando fixture.Build falha, mesmo se a customização injetada especificar não resolver a referência circular.

Perdi algo no changelog ou é um bug?

Desde já, obrigado :)

question

Comentários muito úteis

Você acha que a abordagem do Omitter pode resolver o problema? Eu não conhecia essa aula e com certeza vou tentar de qualquer maneira.

Se você reescrever sua personalização da seguinte forma, todos os testes começarão a passar:

c# public class DummyCustomization : ICustomization { public void Customize(IFixture fixture) { // Don't populate the DummyObject.CircularRef property. fixture.Customizations.Add( new Omitter( new EqualRequestSpecification( typeof(DummyObject).GetProperty(nameof(DummyObject.CircularRef))))); } }

Portanto, sim, isso deve resolver o seu problema 😉 Além disso, essa abordagem não parece muito complicada, então deve ser aceitável.

Informe-nos se tiver mais perguntas sobre a sua questão.

Todos 5 comentários

@ Dev-I-Ant Obrigado por postar o problema aqui e por uma descrição detalhada 👍

Bem, este comportamento é resultado das mudanças aplicadas em # 781. Corrigi o problema em que uma personalização pode afetar outras personalizações. Isso acontece mesmo se você tiver duas personalizações subsequentes para o mesmo tipo - a anterior será totalmente ignorada.

Ao usar a API Build<T>() , você está, na verdade, criando uma personalização temporária para o tipo DummyObject . Isso significa que todas as personalizações anteriores (aplicadas por meio da API Customize<DummyObject>() ) são ignoradas e não participam. É por isso que você vê novamente o erro sobre a dependência circular.

Este tipo de isolamento é necessário, caso contrário, podem ocorrer problemas muito estranhos (consulte os problemas mencionados no PR mencionado acima). Portanto, para manter as coisas simples, eu diria que o comportamento observado não é um bug, mas uma particularidade do design que temos atualmente.


Para atenuar o problema que você observa, vejo basicamente duas maneiras.

Solução alternativa 1: adicione Omitter para a propriedade

Já tínhamos uma pergunta semelhante, então você pode reutilizar a abordagem sugerida aqui ou alterá-la para melhor atender às suas necessidades. Obviamente, se esse for um cenário comum em seu projeto, você poderia criar um método de extensão para realizar a tarefa com mais facilidade.

Solução alternativa 2: use a API Create<T>()

Em sua amostra, não há necessidade de usar a API Build<> . Claro, pode ser que você tenha fornecido uma amostra simplificada e, na verdade, precise dessa API. No entanto, decidi fornecer essa opção para o caso de 😃

Deixe-me saber se minha resposta ajudou a lançar alguma luz sobre este problema 😉

@zvirja Obrigado por seus insights, isso torna as coisas mais claras.

Já li muito sobre o fato de que empilhar personalizações no mesmo tipo não é possível (mesmo que seja frustrante), e essa mudança certamente me causará alguns problemas, mas entendo por que você teve que mudar algo.

Meu caso de uso é testar um aplicativo usando um grande gráfico de objetos que muitas referências circulares; o padrão (anti -?) típico em que um pai mantém uma lista de filhos que, por sua vez, fazem referência a seus pais.

Adoraria mudar o modelo, mas não é possível, pelo menos por enquanto.

Portanto, simplesmente criar objetos usando fixture.Create não é uma opção e geralmente os construímos chamando OmitAllProperties logo depois, o que limita o poder do AF.

Então, eu costumava criar algumas personalizações que tratavam disso para mim, omitindo as referências circulares (então os desenvolvedores apenas tinham que construir seus objetos sem ter que se preocupar se havia referências circulares ou não, e ser capaz de personalizá-los ainda mais usando Com, Sem e assim por diante). Mas essa mudança meio que quebra tudo isso.
Você acha que a abordagem do Omitter pode resolver o problema? Eu não conhecia essa aula e com certeza vou tentar de qualquer maneira.

Obrigado novamente e, por favor, deixe-me saber se você acha que pode adicionar algo ao meu reflexo.

Você acha que a abordagem do Omitter pode resolver o problema? Eu não conhecia essa aula e com certeza vou tentar de qualquer maneira.

Se você reescrever sua personalização da seguinte forma, todos os testes começarão a passar:

c# public class DummyCustomization : ICustomization { public void Customize(IFixture fixture) { // Don't populate the DummyObject.CircularRef property. fixture.Customizations.Add( new Omitter( new EqualRequestSpecification( typeof(DummyObject).GetProperty(nameof(DummyObject.CircularRef))))); } }

Portanto, sim, isso deve resolver o seu problema 😉 Além disso, essa abordagem não parece muito complicada, então deve ser aceitável.

Informe-nos se tiver mais perguntas sobre a sua questão.

Isso funciona perfeitamente. Muito obrigado! Acho que você pode fechar isso então :)

@ Dev-I-Ant Awesome, obrigado pelo feedback! :)

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

ploeh picture ploeh  ·  3Comentários

tiesmaster picture tiesmaster  ·  7Comentários

mjfreelancing picture mjfreelancing  ·  4Comentários

zvirja picture zvirja  ·  4Comentários

JoshKeegan picture JoshKeegan  ·  6Comentários