Autofixture: La creación de una instancia personalizada falla en referencias circulares

Creado en 13 ene. 2018  ·  5Comentarios  ·  Fuente: 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)
            );
        }
    }

Acabo de actualizar AutoFixture de 3.50.6 a 4.0.0.

El código anterior funciona perfectamente en 3.50.6, pero solo la primera prueba (usando fixture.Create) pasa usando 4.0.0 (.NET 4.6.2, NUnit3.7.1, FluentAssertions 4.19.4).

La prueba que usa fixture.Build falla, incluso si la personalización inyectada especifica no resolver la referencia circular.

¿Me perdí algo en el registro de cambios o es un error?

Gracias por adelantado :)

question

Comentario más útil

¿Crees que el enfoque Omitter puede resolver el problema? No conocía esta clase y definitivamente la intentaré de todos modos.

Si reescribe su personalización de la siguiente manera, todas las pruebas comenzarán a pasar:

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

Por lo tanto, sí, debería solucionar su problema 😉 Además, este enfoque no parece demasiado engorroso, por lo que debería ser aceptable.

Por favor, avíseme si tiene más preguntas sobre su pregunta.

Todos 5 comentarios

@ Dev-I-Ant Gracias por publicar el problema aquí y por una descripción tan detallada 👍

Bueno, este comportamiento es el resultado de los cambios aplicados en # 781. Solucioné el problema cuando una personalización podía afectar a otras personalizaciones. Eso sucede incluso si tiene dos personalizaciones posteriores para el mismo tipo; la anterior se ignorará por completo.

Cuando usa la API Build<T>() , de hecho está creando una personalización temporal para el tipo DummyObject . Eso significa que todas las personalizaciones anteriores (aplicadas a través de la API Customize<DummyObject>() ) se ignoran y no participan. Es por eso que vuelve a ver un error sobre la dependencia circular.

Se requiere este tipo de aislamiento; de lo contrario, podrían ocurrir problemas muy extraños (consulte los problemas a los que se hace referencia en el PR mencionado anteriormente). Por lo tanto, para simplificar las cosas, diría que el comportamiento observado no es un error, sino una particularidad del diseño que tenemos actualmente.


Para mitigar el problema que observa, veo básicamente dos formas.

Solución 1: agregue Omitter para la propiedad

Ya teníamos una pregunta similar, por lo que podría reutilizar el enfoque sugerido aquí o modificarlo para que se adapte mejor a sus necesidades. Obviamente, si ese es un escenario común en su proyecto, podría crear un método de extensión para lograr la tarea más fácilmente.

Solución 2: utilice la API Create<T>() lugar

En su muestra, no es necesario utilizar la API Build<> . Por supuesto, es posible que haya proporcionado una muestra simplificada y, de hecho, necesite esa API. Sin embargo, decidí brindarte esta opción por si acaso 😃

Déjame saber si mi respuesta ayudó a arrojar algo de luz sobre este tema 😉

@zvirja Gracias por sus ideas, esto aclara las cosas.

Ya leí mucho sobre el hecho de que no es posible apilar personalizaciones en el mismo tipo (incluso si es frustrante), y este cambio ciertamente me dará algunos problemas, pero entiendo por qué tuvo que cambiar algo.

Mi caso de uso es probar una aplicación usando un gran gráfico de objetos con muchas referencias circulares; el patrón típico (anti -?) donde un padre tiene una lista de hijos que a su vez hacen referencia a su padre.

Me encantaría cambiar de modelo pero no es posible, al menos por ahora.

Entonces, simplemente crear objetos usando fixture.Create no es una opción y generalmente los construimos llamando a OmitAllProperties justo después, lo que limita el poder de AF.

Así que solía crear algunas personalizaciones que manejaban eso por mí, omitiendo las referencias circulares (luego los desarrolladores solo tenían que construir sus objetos sin tener que preocuparse de si había referencias circulares o no, y poder personalizarlas aún más usando Con, Sin y así sucesivamente). Pero este cambio rompe todo esto.
¿Crees que el enfoque Omitter puede resolver el problema? No conocía esta clase y definitivamente la intentaré de todos modos.

Gracias de nuevo, y avíseme si cree que puede agregar algo a mi reflejo.

¿Crees que el enfoque Omitter puede resolver el problema? No conocía esta clase y definitivamente la intentaré de todos modos.

Si reescribe su personalización de la siguiente manera, todas las pruebas comenzarán a pasar:

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

Por lo tanto, sí, debería solucionar su problema 😉 Además, este enfoque no parece demasiado engorroso, por lo que debería ser aceptable.

Por favor, avíseme si tiene más preguntas sobre su pregunta.

Esto funciona perfectamente. ¡Muchas gracias! Supongo que puedes cerrar esto entonces :)

@ Dev-I-Ant Awesome, ¡gracias por los comentarios! :)

¿Fue útil esta página
0 / 5 - 0 calificaciones