Archive for September 18, 2009

XUnit.NET Extensions, Trace y AutoRollback

A veces es necesario (por alguna razón extraña), mostrar el inicio y el fin de un test en la consola, con el tiempo de inicio y tiempo de fin (imaginémonos un test de performance sencillo); para esos menesteres tenemos a nuestro amigo TraceAttribute, que no hace más que enviar a la salida Trace el inicio y el fin del test, un atributo sencillo de entender.

[Fact, Trace]
public void Must_sent_to_trace_console_start_and_end_time_of_test()
{
    Assert.True(true);
}
AutoRollbackAttribute

Comunmente cuando trabajamos con bases de datos, es bastante común el toparnos con transacciones, o mejor aún, toparnos con código que para simplificarnos las cosas debería correr dentro de una transacción. Por ejemplo, un test contra la base de datos que haga ciertos inserts, pero al final del test no nos importa el resultado y simplemente descartamos la transacción, por lo tanto nada fue insertado o modificado en la base de datos. Para esas tareas tenemos al atributo AutoRollbackAttribute, que simplemente al inicio del test crea una transacción y al final del test descarta la transacción. Es dificil probar código transaccional, pero con la “descripción” de que realiza el test, simplemente podemos ver si hay o no una transacción creada.

// NOTE: It Requires referece to System.Transactions
[Fact]
public void When_no_autorollback_is_present_there_is_no_transaction()
{
    Assert.Null(Transaction.Current);
}

[Fact, AutoRollback]
public void When_autorollback_is_present_there_is_a_transaction()
{
    Assert.NotNull(Transaction.Current);
}

XUnit.NET Extensions, AssumeIdentity

Una de las excelentes características y habilidades de XUnit.NET es su capacidad de ser extendido en muchas formas. Como parte del “paquete” de XUnit.NET, se incluye una librería adicional con atributos extras para cambiar en cierta manera el comportamiento de un test mientras realizamos las pruebas. A esta librería se le llama XUnit.NET Extensions y viene incluída con el download de XUnit.NET. Veremos algunos de los atributos extras incluídos con XUnit.NET Extensions.

El primero en ver será AssumeIdentity, este atributo permite cambiar el Principal de la actual Thread de ejecución y agregarle el rol indicado por el atributo. Por defecto la thread de ejecución del test corre bajo el principal con la identity de “xunit”.

[Fact]
public void It_should_fail_because_user_has_no_role()
{
    Assert.False(Thread.CurrentPrincipal.IsInRole("fake_role"));
}

[Fact]
[AssumeIdentity("fake_role")]
public void It_should_pass_because_user_is_in_role()
{
    Assert.True(Thread.CurrentPrincipal.IsInRole("fake_role"));
}

Voilà! Luego converaremos de los otros atributos disponibles en este namespace.

XUnit.NET y las Excepciones

El día de ayer publiqué mi primer post corto acerca de XUnit.Net, e inmediatamente después alguien me hizo una pregunta, la cuál parafraseando va algo así:

“Puedo testear o ver el mensaje retornado por la excepción y así asegurarme que es una excepción específica”

No voy a discutir si esto esta bien o mal (en cuanto a la especificidad de la excepción), pero me parece interesante la pregunta. Como mencionamos ayer, podemos probar que una excepción es arrojada mediante el método “Throws” de la clase Assert:

public void ThrowOperation()
{
    throw new InvalidOperationException("My message");
}

[Fact]
public void TestAssertWithException()
{
    Assert.Throws<InvalidOperationException>(() => ThrowOperation());
}

El detalle es que Assert.Throws no solamente se asegura que la excepción fue arrojada, sino que efectivamente retorna la excepción al sistema como un retorno más, de esta manera podemos hacer cosas como:

public void ThrowOperation()
{
    throw new InvalidOperationException("My message");
}

[Fact]
public void TestAssertWithMessage()
{
    var ex = Assert.Throws<InvalidOperationException>(() => ThrowOperation());
    Assert.Equal("My message", ex.Message);
}

De esta manera no sólo podemos probar que la excepción es efectivamente arrojada, sino que también podemos probar sin ningún problema otros atributos de la excepción como tal. ¿Interesante verdad?

Cómo siempre, cualquier pregunta o comentario somos oidos abiertos :)

XUnit.NET y las Colecciones

Continuando con mi corta serie acerca de XUnit, algo que es sumamente útil en las pruebas es verificar si un item se encuentra o no en una colección. En XUnit.Net esto es sumamente fácil, simplemente usamos los métodos Contains y DoesNotContains los cuales también funcionan con strings.

private readonly int[] numbers = {1, 2, 3, 4, 5, 6};

[Fact]
public void It_must_return_true_if_number_is_in_collection()
{
    Assert.Contains(1, numbers);
}

[Fact]
public void It_must_return_true_if_number_is_not_in_collection()
{
    Assert.DoesNotContain(7, numbers);
}

[Fact]
public void It_must_return_true_if_string_contains_substring()
{
    Assert.Contains("hola", "hola mundo");
}

[Fact]
public void It_must_return_true_if_string_does_not_contains_substring()
{
    Assert.DoesNotContain("adios", "hola mundo");
}

De igual manera tenemos métodos para verificar si una colección se encuentra vacía o no, obviamente nuestros métodos en cuestion seran Empty y NotEmpty (estos no funcionan con strings).

private int[] numbers = { 1, 2, 3, 4, 5, 6 };

[Fact]
public void It_must_return_true_if_collection_is_not_empty()
{
    Assert.NotEmpty(numbers);
}

[Fact]
public void It_must_return_true_if_collection_is_empty()
{
    int[] empty = {};
    Assert.Empty(empty);
}

XUnit.NET y un par de Assert extras

Un par de Assert’s extras que vienen con XUnit.NET y son sumamente útiles, son aquellos capaces de trabajar con rangos, y estos pueden usar nuestros IComparer<T>, como siempre, nada explica mejor que un FactSet

[Fact]
public void It_must_assert_number_is_in_a_range_of_numbers()
{
    Assert.InRange(1, 1, 3);
    Assert.InRange(2, 1, 3);
    Assert.InRange(3, 1, 3);
    Assert.NotInRange(4, 1, 3);
    Assert.NotInRange(0, 1, 3);
}

[Fact]
public void It_must_assert_letter_is_in_a_range_of_letters()
{
    Assert.InRange("a", "a", "d");
    Assert.NotInRange("e", "a", "d");
    Assert.NotInRange("B", "a", "b");
    Assert.InRange("B", "a", "b", StringComparer.InvariantCultureIgnoreCase);
}

Otro aspecto que solemos probar es si una instancia es asignable, del tipo o derivada (implementada) a partir de otro tipo, en otras palabras, si hereda o implementa a alguien más. Esto es facil con IsType e IsAssignableFrom

[Fact]
public void It_must_assert_object_is_of_same_type()
{
    var item = new MySimpleEntity();
    Assert.IsType<MySimpleEntity>(item);
    Assert.IsNotType<IDoable>(item);
}

[Fact]
public void It_must_assert_object_is_assignable_from_type()
{
    var item = new MySimpleEntity();
    Assert.IsAssignableFrom<IDoable>(item);
}

Y por último, a veces necesitamos probar que una instancia representa la misma instancia que otra, no precisamente que es “igual”, esto lo logramos con “Same”:

[Fact]
public void It_must_assert_object_is_the_same_instance()
{
    var item = new MySimpleEntity();
    var other = item;
    var another = new MySimpleEntity();

    Assert.Same(item, other);
    Assert.NotSame(item, another);
}

Bien, continuaremos luego con más XUnit.NET, todo esto como un empujon para animarlos a probar TDD y en especial esta Testing Framework sumamente útil :)

Saludos!