Pregunta

Me empezó a utilizar AutoFixture http://autofixture.codeplex.com/ como mis pruebas de unidad estaba hinchado con una gran cantidad de datos de configuración. Yo era pasar más tiempo en proponiéndose seguridad de los datos que escribir mi unidad de prueba. Aquí está un ejemplo de cómo se ve mi inicial de los exámenes de unidad como (ejemplo tomado de la muestra de la aplicación de carga del libro azul DDD)

[Test]
public void should_create_instance_with_correct_ctor_parameters()
{
    var carrierMovements = new List<CarrierMovement>();

    var deparureUnLocode1 = new UnLocode("AB44D");
    var departureLocation1 = new Location(deparureUnLocode1, "HAMBOURG");
    var arrivalUnLocode1 = new UnLocode("XX44D");
    var arrivalLocation1 = new Location(arrivalUnLocode1, "TUNIS");
    var departureDate1 = new DateTime(2010, 3, 15);
    var arrivalDate1 = new DateTime(2010, 5, 12);

    var carrierMovement1 = new CarrierMovement(departureLocation1, arrivalLocation1, departureDate1, arrivalDate1);

    var deparureUnLocode2 = new UnLocode("CXRET");
    var departureLocation2 = new Location(deparureUnLocode2, "GDANSK");
    var arrivalUnLocode2 = new UnLocode("ZEZD4");
    var arrivalLocation2 = new Location(arrivalUnLocode2, "LE HAVRE");
    var departureDate2 = new DateTime(2010, 3, 18);
    var arrivalDate2 = new DateTime(2010, 3, 31);

    var carrierMovement2 = new CarrierMovement(departureLocation2, arrivalLocation2, departureDate2, arrivalDate2);

    carrierMovements.Add(carrierMovement1);
    carrierMovements.Add(carrierMovement2);

    new Schedule(carrierMovements).ShouldNotBeNull();
}

Así es como he tratado de refactorearlo con AutoFixture

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

    fixture.Register(() => new UnLocode(UnLocodeString()));

    var departureLoc = fixture.CreateAnonymous<Location>();
    var arrivalLoc = fixture.CreateAnonymous<Location>();
    var departureDateTime = fixture.CreateAnonymous<DateTime>();
    var arrivalDateTime = fixture.CreateAnonymous<DateTime>();

    fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
        (departure, arrival, departureTime, arrivalTime) => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime));

    var carrierMovements = fixture.CreateMany<CarrierMovement>(50).ToList();

    fixture.Register<List<CarrierMovement>, Schedule>((carrierM) => new Schedule(carrierMovements));

    var schedule = fixture.CreateAnonymous<Schedule>();

    schedule.ShouldNotBeNull();
}

private static string UnLocodeString()
{
    var stringBuilder = new StringBuilder();

    for (int i = 0; i < 5; i++)
        stringBuilder.Append(GetRandomUpperCaseCharacter(i));

    return stringBuilder.ToString();
}

private static char GetRandomUpperCaseCharacter(int seed)
{
    return ((char)((short)'A' + new Random(seed).Next(26)));
}

Me gustaría saber si hay una mejor manera de refactorizar. Le gustaría hacerlo más corto y más fácil que eso.

¿Fue útil?

Solución

Su intento inicial se ve bien, pero hay al menos un par de cosas que puede simplificar un poco.

En primer lugar, usted debe ser capaz de reducir la siguiente:

fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
    (departure, arrival, departureTime, arrivalTime) =>
        new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime));

a esto:

fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
    () => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime));

ya que no está usando esas otras variables. Sin embargo, esto encierra esencialmente cualquier creación de CarrierMovement utilizar los mismos cuatro valores. Aunque cada CarrierMovement creado será una instancia independiente, todos ellos comparten los mismos cuatro valores, y me pregunto si eso era lo que quería decir?

En el mismo sentido que el anterior, en lugar de

fixture.Register<List<CarrierMovement>, Schedule>((carrierM) =>
    new Schedule(carrierMovements));

puede escribir

fixture.Register(() => new Schedule(carrierMovements));

ya que no utiliza la variable carrierM. Tipo de inferencia se darán cuenta de que está registrando un horario debido al tipo de retorno de la Func.

Sin embargo, en el supuesto de que las miradas Horario constructor de esta manera:

public Schedule(IEnumerable<CarrierMovement> carrierMovements)

en su lugar podría haber acaba de registrar la carrierMovements como esto:

fixture.Register<IEnumerable<CarrierMovement>>(carrierMovements);

lo que causaría AutoFixture para resolver automáticamente Horario correctamente. Este enfoque es más fácil de mantener, ya que le permite añadir un parámetro al constructor Schedule en el futuro sin romper la prueba (siempre y cuando AutoFixture puede resolver el tipo de parámetro).

Sin embargo, podemos hacer algo mejor en este caso porque realmente no usamos la variable carrierMovements para nada más que la de registrarse. Lo que realmente necesitamos es sólo para decirle AutoFixture cómo crear instancias de IEnumerable<CarrierMovement>. Si no se preocupan por el número 50 (no debería), incluso podemos usar la sintaxis de grupo Método de la siguiente manera:

fixture.Register(fixture.CreateMany<CarrierMovement>);

Aviso la falta de parantheses de invocación de método:. Estamos registrando un Func, y puesto que el método devuelve CreateMany<T> tipo IEnumerable<T> inferencia se encarga del resto

Sin embargo, esos son todos los detalles. En un nivel superior, es posible que desee considerar no registrar CarrierMovement en absoluto. Suponiendo que este constructor:

public CarrierMovement(Location departureLocation,
    Location arrivalLocation,
    DateTime departureTime,
    DateTime arrivalTime)

autofixture debe ser capaz de averiguar por sí mismo.

Se va a crear una nueva instancia de localización de cada departureLocation y arrivalLocation, pero eso no es diferente de lo que lo hizo de forma manual en la prueba inicial.

Cuando se trata de las veces, por defecto usos AutoFixture DateTime.Now, que al menos se asegura que el tiempo de llegada no será antes de la hora de salida. Sin embargo, son muy propensos a ser idénticos, pero siempre se podrían registrar una función de incremento automático si eso es un problema.

Teniendo en cuenta estas consideraciones, aquí hay una alternativa:

public void should_create_instance_with_correct_ctor_parameters_AutoFixture()
{
    var fixture = new Fixture();

    fixture.Register(() => new UnLocode(UnLocodeString()));

    fixture.Register(fixture.CreateMany<CarrierMovement>);

    var schedule = fixture.CreateAnonymous<Schedule>();

    schedule.ShouldNotBeNull();
}

Para resolver el problema con IList<CarrierMovement> tendrá que registrarlo. He aquí una manera de hacerlo:

fixture.Register<IList<CarrierMovement>>(() =>
    fixture.CreateMany<CarrierMovement>().ToList());

Sin embargo, ya que preguntas, quiero decir que las miradas Horario constructor de esta manera:

public Schedule(IList<CarrierMovement> carrierMovements)

y realmente creo que debería reconsiderar el cambio de esa API para tomar una IEnumerable<Carriemovement>. Desde una perspectiva de diseño de API, el suministro de una colección a través de cualquier miembro (incluyendo un constructor) implica que el miembro está autorizado a modificar la colección (por ejemplo, invocando el mismo de añadir, eliminar y métodos claros). Eso es casi el comportamiento que se espera de un constructor, así que no lo permite.

AutoFixture generará automáticamente nuevos valores para todos los objetos Location en mi ejemplo anterior, pero debido a la velocidad de la CPU, las instancias posteriores de DateTime es probable que sean idénticas.

Si desea aumentar DateTime, se puede escribir una pequeña clase que incrementa el devueltos DateTime cada vez que se invoca. Voy a dejar la aplicación de esa clase para el lector interesado, pero que entonces podía registrarlo, así:

var dtg = new DateTimeGenerator();
fixture.Register(dtg.Next);

suponiendo esta API (aviso una vez más la sintaxis Grupo método anterior):

public class DateTimeGenerator
{
    public DateTime Next();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top