Autofixture: La aplicación de personalizaciones desde la clase base o la interfaz falla en 4.0.0-rc1 (regresión)

Creado en 20 nov. 2017  ·  10Comentarios  ·  Fuente: AutoFixture/AutoFixture

Las siguientes pruebas tienen éxito en AutoFixture 3.51.0 pero fallan en 4.0.0-rc1 :
(incluso en la última confirmación en el maestro: c856cd6 )

`` c #
[Hecho]
public void ShouldApplyCustomizationsFromBaseClass ()
{
var fixture = new Fixture ();
accesorio Personalizar(c => c. Sin (bc => bc.Id));
var res = fixture.Create();
Assert.Equal (0, res.Id);
}

[Hecho]
public void ShouldApplyCustomizationsFromInterface ()
{
var fixture = new Fixture ();
accesorio Personalizar(c => c. Sin (bc => bc.Id));
var res = fixture.Create();
Assert.Equal (0, res.Id);
}

interfaz pública IInterface
{
int Id {obtener; colocar; }
}

clase pública BaseClass
{
public int Id {obtener; colocar; }
}

clase pública ImplClass: BaseClass, IInterface
{

}
''

question

Todos 10 comentarios

Gracias por plantear la pregunta. Bueno, este es el cambio deseado y se ha aplicado a propósito.

Anteriormente, teníamos un problema cuando la personalización de un subtipo podía afectar a otro subtipo. Por ejemplo:

`` c #
class Base {cadena pública Common {get; colocar; }}
clase Child1: Base {}
clase Child2: Base {}

[Hecho]
public void GuaranteeCustomizationAreNotAffected ()
{
var fixture = new Fixture ();
accesorio Personalizar(c => c. Con (x => x.Común, "ficticia"));

var result = fixture.Create<Child2>();

Assert.NotNull(result.Common);

}


In the v3 this test failed, causing a lot of confusion to people. As you might imagine, the scenarios were more complicated and it was very non-obvious why the particular members are not initialized. 

For instance, the following code will not work, which again proves that API is not designed for that.
```c#
fixture.Customize<Base>(c => c.With(x => x.Common, "foo"));

Por lo tanto, para solucionar ese problema , cambiamos el enfoque , de modo que ahora la personalización de un tipo no puede afectar a otros tipos, incluso si pertenecen a la misma línea de herencia. Como puede ver en ese PR, solucionó un montón de problemas de usabilidad y agregó más claridad (mientras que el cambio se está rompiendo).

Si aún desea omitir las propiedades de la interfaz / base, puede usar el siguiente fragmento:
`` c #
clase privada SamePropertySpecification: IRequestSpecification
{
privado de solo lectura Type _declaringType;
cadena privada de solo lectura _name;

public SamePropertySpecification(Type declaringType, string name)
{
    _declaringType = declaringType;
    _name = name;
}

public bool IsSatisfiedBy(object request)
{
    if (request is PropertyInfo pi)
    {
        return pi.DeclaringType == this._declaringType &&
               pi.Name.Equals(this._name, StringComparison.Ordinal);
    }

    return false;
}

}

[Hecho]
public void TestBasePropertyOmitting ()
{
var fixture = new Fixture ();

fixture.Customizations.Add(new Omitter(new SamePropertySpecification(typeof(Base), nameof(Base.Common))));

var result = fixture.Create<Child1>();
Assert.Null(result.Common);

}
''

Si necesita esa función con frecuencia, puede crear su propio método de extensión por fixture . Sin embargo, votaría por no tener una función de este tipo lista para usar, ya que parece muy confuso.

Ok, bueno saber que fue intencional. Definitivamente estoy de acuerdo en que fue confuso, pero si se puede aplicar explícitamente, es una buena característica. Probaré su fragmento (y probablemente haga el método de extensión).

Por cierto: ¿Es esto algo que desea agregar a la lista de cambios importantes? Puede evitar que se haga la misma pregunta.

No hay forma de aceptar el comportamiento anterior para una personalización de todas las personalizaciones, ¿verdad?

¿Es esto algo que desea agregar a la lista de cambios importantes?

Bueno, se menciona en la sección de corrección de errores . De hecho, esto no es un cambio rotundo, ya que el comportamiento anterior nunca se declaró como una "característica". Más bien, ese fue un efecto secundario no deseado y finalmente se eliminó 😉

No hay forma de aceptar el comportamiento anterior para una personalización de todas las personalizaciones, ¿verdad?

No, no hay tal manera en la que creo, ya que modifiqué totalmente el enfoque.

Pruebe el fragmento y, si funciona, no dude en cerrar el problema. O avíseme si tiene más preguntas 😃

Lo modifiqué para que también funcione para interfaces e hice un método de extensión para que pueda usarse en 'Fixture.Customize(...) '. Se siente un poco más complicado de lo que me gustaría, especialmente porque la forma en que funcionó en la v3 fue perfecta para nosotros. Pero dado que nuestro único caso de uso es omitir propiedades y (lo más importante 😉) funciona, funcionará. ¡Gracias!

@nphmuller Lamento haber roto tu código ordenado y ordenado y ahora necesitas sobrevivir 😄 Pero creo que tu sacrificio fue hecho por el bien de todos nosotros, así que no nos culparás demasiado 😅

No tengo ninguna culpa. Comprenda perfectamente su razonamiento e incluso esté de acuerdo. Solo apesta para mi 😉

Podría sumergirme en el problema más tarde para ver si hay una manera más ordenada. Por ahora está bien.

Solo por curiosidad, ¿cuál es su caso de uso real para eso? Porque nunca llegué a satisfacer una necesidad en este tipo de funcionalidad. Por lo general, cuando escribo una prueba, necesito personalizar solo un tipo específico, en lugar de configurar el tipo base o de interfaz. ¿Escribes algún tipo de personalización global?

En este caso usamos AutoFixture como un generador de datos de prueba para las pruebas de integración de Entity Framework.

Tenemos un par de tipos base o interfaces que utilizan nuestras entidades y que contienen propiedades comunes. Id, tenant-id (y propiedad de navegación), cosas así.

Por ejemplo, una de estas propiedades es qué usuario creó el objeto. Esta propiedad no es tan interesante para la mayoría de las pruebas y, si se generara, crearía un gráfico enorme (la clase User también tiene muchas propiedades de navegación) que tendría que generarse e insertarse en la base de datos. Esto haría que la prueba fuera lenta o incluso fallaría a nivel de base de datos porque el objeto no estaba destinado a insertarse de esa manera.

Por lo tanto, es más fácil simplemente excluir estas propiedades y participar cuando sea necesario. En el nivel de clase base, porque de lo contrario, se tendría que escribir la misma regla de omisión para cada tipo de entidad.

@nphmuller Gracias por la aclaración, ahora está claro 😉 Sí, parece que el escenario tiene sentido, pero debo admitir que parece algo que rara vez necesitas.

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

Temas relacionados

tiesmaster picture tiesmaster  ·  7Comentarios

malylemire1 picture malylemire1  ·  7Comentarios

TroyHouston picture TroyHouston  ·  6Comentarios

JoshKeegan picture JoshKeegan  ·  6Comentarios

ploeh picture ploeh  ·  3Comentarios