Desde hace tiempo tengo ganas de escribir una serie de posts acerca de TDD y BDD, pero como siento que soy aún un minúsculo saltamontes en el gigantesco mundo de testing, decidí mejor comenzar por algo sumamente básico, qué tal si hablamos de XUnit.Net?
XUnit.Net es una testing framework creada por James Newkirk (ex-desarrolladore de NUnit) y Brad Wilson (ASP.NET developer en Microsoft) la cual agrega excelentes características a nuestra forma de realizar tests.
- Una sola instancia de test object por método del test
- No atributos de TearDown o SetUp, de tal manera que se usan los constructores de clase para inicializar el test y Dispose de IDisposable para finalizar el test
- Sin atributos de ExpectedException, en vez de eso las excepciones son tratadas como un verdadero assert de la forma Assert.Throws
- Uso extensivo de características de la Framework 2 en adelante (generics, delegados, expresiones lambda)
- Número pequeño de atributos
- Totalmente extensible (esta es la parte interesante)
Un típico test en NUnit podría lucir algo así:
[TestFixture]
class TddSample
{
private string text = string.Empty;
private DisposableObject objectToDestroy;
[SetUp]
public void SetUp()
{
objectToDestroy = new ObjectToDestroy();
}
[TearDown]
public void TearDown()
{
objectToDestroy.Dispose();
}
[Test]
public void It_must_be_initialized_with_a_simple_text_in_ctor()
{
Assert.IsNotNull(objectToDestroy);
}
[Test]
public void It_should_add_two_numbers()
{
var result = 1 + 1;
Assert.AreEqual(2, result);
}
[Test]
[ExpectedException(typeof(DivideByZeroException))]
public void It_throws_when_a_number_is_divided_by_zero()
{
int i = 0;
int j = 1;
var result = j / i;
}
[Test]
[Ignore("I want to break free!")]
public void It_must_skip_this_test()
{
Assert.True(false, "This test would fail");
}
}
Un equivalente en Xunit.Net podría verse así:
public class SimpleXunitTest : IDisposable
{
private readonly string text = string.Empty;
private readonly DisposableObject objectToDispose;
public SimpleXunitTest()
{
text = "hello world";
objectToDispose = new DisposableObject();
}
[Fact]
public void It_must_be_initialized_with_a_simple_text_in_ctor()
{
Assert.False(string.IsNullOrEmpty(text));
}
[Fact]
public void It_should_add_two_numbers()
{
int result = 1 + 1;
Assert.Equal(2, result);
}
[Fact]
public void It_throws_when_a_number_is_divided_by_zero()
{
int i = 0;
int j = 1;
Assert.Throws<DivideByZeroException>(() => j/i);
}
[Fact(Skip = "I want to break free!")]
public void It_must_skip_this_test()
{
Assert.True(false, "This test would fail");
}
public void Dispose()
{
objectToDispose.Dispose();
}
}
En el sitio del proyecto de XUnit.Net en Codeplex podemos encontrar más equivalencias entre NUnit y XUnit.Net. Una de las verdaderas riquezas de XUnit.Net (a mi criterio muy personal) es la librería adjunta con extensiones conocida como xunit.extensions.dll que contiene atributos especiales que no forman parte a simple vista de la framework de testing, pero nos pueden ayudar mucho en nuestras fixtures, por ejemplo: FreezeClock, o las grandiosas [Theory] con soporte para data inline en nuestros tests
public class SimpleXunitTest
{
[Theory]
[InlineData(1, 2, 3)]
[InlineData(2, 2, 4)]
[InlineData(4, 5, 9)]
public void It_should_add_two_numbers_correctly(int a, int b, int c)
{
var result = a + b;
Assert.Equal(c, result);
}
}
La documentación indica varias formas de incluir la data para las “teorías”, que van desde la inline más simple hasta usar una base de datos y un archivo de Excel.
En cuanto a runners, XUnit soporta TeamCity, MSBuild, R# (mediante proyecto hermano XUnit.Contrib), TestDriven.Net y muchos otros.
Espero mi pequeña intro acerca de XUnit.Net les anime a probarlo. Saludos!