Pruebas unitarias: Manos a la obra ( II )
En el post anterior vimos una introducción al framework de Visual Studio para el desarrollo de pruebas unitarias. En este post completaré algunos aspectos que no se mencionaron en el post anterior.
En el ejemplo que vimos en el post anterior implementamos una prueba muy simple sobre el método Sumar.
[TestMethod()]
public void SumarTest()
{
ClaseEjemplo target = new ClaseEjemplo();
int a = 1;
int b = 2;
int expected = 3;
int actual;
actual = target.Sumar(a, b);
Assert.AreEqual(expected, actual);
}
En la prueba podéis ver cómo usando la sentencia Assert.AreEqual se comprueba si el resultado del método Sumar corresponde con el resultado esperado.
La clase Assert es parte del Framework y que se incluye dentro de la referencia Microsoft.VisualStudio.QualityTools.UnitTestFramework. Esta clase nos proporciona algunos métodos para comprobar los resultados de los métodos que están sometidos a la prueba; IsFalse, IsNull, AreSame…..

Parece por tanto, que toda prueba debería terminar con una clausula de este tipo para comprobar si la prueba ha sido correcta o no. Pues bien, esto no es así.
Aunque seguramente usaremos alguno de los métodos de la clase Assert en la mayoría de las situaciones no es de uso obligatorio. Si no usamos esta clausula, la ejecución de la prueba dará un resultado correcta siempre y cuando la ejecución de la misma no genere una excepción.
Por ejemplo, vamos a modificar nuestro método suma para que sólo sume valores positivos y si alguno de los parámetros que recibe es menor que 0, que devuelva una excepción.
public int Sumar(int a, int b)
{
if (a < 0 || b < 0)
throw new ArgumentException();
return a + b;
}
En este caso, nos podría interesar hacer una prueba dónde comprobemos que no se genera una excepción cuando los parámetros sean mayores que 0. En esa prueba no sería necesario incluir ninguna sentencia de comprobación. Si le pasamos valores positivos la prueba será correcta siempre y cuando no genera una excepción.
Del mismo modo, nos interesará hacer una prueba para comprobar que la validación se hace correctamente y que se devuelve la excepción ArgumentException en este caso.
En este caso, deberemos decorar el método de prueba con el atributo ExpectedException. Con este atributo estamos especificando que el resultado esperado de la ejecución de la prueba es la excepción ArgumentException. Si no se genera la excepción o la excepción no es del tipo esperado, la prueba fallará.
[TestMethod()]
[ExpectedException(typeof(System.ArgumentException))]
public void SumarTest()
{
ClaseEjemplo target = new ClaseEjemplo();
int a = -1;
int b = -2;
int actual;
actual = target.Sumar(a, b);
}
Para terminar con esta introducción hablaremos sobre los métodos de inicialización. En las clases que contienen las pruebas unitarias existen cuatro métodos qué tienen una finalidad especial y que están decorados con un atributo que determina su objetivo.
El atributo ClassInitialize identifica un método que contiene código que debe ejecutarse antes de que se ejecute cualquiera de las pruebas de la clase y para asignar los recursos que utilizará la clase de pruebas.
El atributo TestInitialize identifica un método que contiene código que se ejecuta antes de cada prueba que contiene la clase y se usa para asignar y configurar los recursos que necesitan todas las clase de la prueba.
El atributo ClassCleanup identifica un método que se utilizará después de la ejecución de todas las pruebas de la clase y para liberar los recursos obtenidos por la prueba.
El atributo TestCleanup contiene código que se ejecutará después de ejecutar cada prueba y se puede usar la liberar los recursos obtenidos durante la prueba.
//Use ClassInitialize to run code before running the first test in the class
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
}
//Use ClassCleanup to run code after all tests in a class have run
[ClassCleanup()]
public static void MyClassCleanup()
{
}
//Use TestInitialize to run code before running each test
[TestInitialize()]
public void MyTestInitialize()
{
}
//Use TestCleanup to run code after each test has run
[TestCleanup()]
public void MyTestCleanup()
{
}
Claro está, el uso de estos métodos no es para nada obligatorio y sólo deberemos emplearlos en las situaciones que lo necesitemos. Por defecto ,al generar la prueba, estos métodos están comentados.
Como ya vimos en los post anteriores una prueba unitaria debería funcionar de manera independiente al entorno y una vez ejecutada dejarlo tal y como estaba antes de ejecutar la prueba.
Estos cuatro métodos nos ayudarán a cumplir con estos dos objetivos ya que nos podrán ayudar a preparar el entorno y los recursos necesarios para la ejecución de las pruebas y a restaurar el entorno y liberar los recursos una vez ejecutadas.