sábado, 5 de diciembre de 2009

Pruebas de Unidad: JUnit 4 y Netbeans IDE

Muchas veces en la incansable labor de desarrollo de software, nos vemos en la necesidad de probar lo que hemos hecho. La forma que primero se nos viene a la cabeza es correr la aplicación, cargar los datos, y ver que todo ande bien.
Eventualmente no tendremos la posibilidad de ejecutar la aplicación completa, o la ejecución de ésta es algo que consume mucho tiempo y es muy laborioso, sin contar que a veces dependemos de necesitar listo el trabajo de otras personas para probar nuestro código. Ahí es donde cobra sentido la automatización de las pruebas de unidad.

La herramienta propuesta por Netbeans IDE y que ciertamente es la más utilizada en el ambiente es jUnit. En este post presentaré las nociones básicas de uso de la versión 4 de jUnit, basado en Annotations.

Utilizar esta herramienta es realmente sencillo, utilizaré la siguiente clase (realmente trivial) como sujeto de pruebas:

public class Matematica {
public int dividir(int a, int b) {
return a / b;
}
}

Para comenzar, creo dentro de mi proyecto de netbeans, en test packages, mi clase que será la Suite de pruebas (o conjunto de pruebas seleccionadas). Cada prueba se realiza dentro de un método y este deberá estar anotado con @Test.

Nuestra primera clase de prueba es esta:

import org.junit.Test;
import org.junit.Assert;

public class PruebaMate {

@Test
public void pruebaCero() {
int valor = new Matematica().dividir(4, 2);
Assert.assertEquals(2, valor);
}

}

A esta clase le iremos agregando cosas a medida que vayamos avanzando.
Para comenzar la prueba en netbeans, click derecho a la clase de prueba dentro del explorador de proyectos y le damos la opción "Run".

Presentando la siguiente salida:

Testsuite: PruebaMate
Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0,089 sec

test:
BUILD SUCCESSFUL (total time: 1 second)

Esto nos dice que la prueba se ejecutó, y no sólo eso sino que se realizó correctamente y el tiempo que tardó.

Ahora agregaremos el siguiente método para probar como se comporta con la división por 0:

@Test
public void pruebaUno() {
new Matematica().dividir(4, 0);
}

Y al correrlo obtenemos la siguiente salida:

Testsuite: PruebaMate
Tests run: 2, Failures: 0, Errors: 1, Time elapsed: 0,061 sec

Testcase: pruebaUno(PruebaMate): Caused an ERROR
/ by zero
java.lang.ArithmeticException: / by zero
at Matematica.dividir(Matematica.java:3)
at PruebaMate.pruebaUno(PruebaMate.java:14)


Test PruebaMate FAILED
test:
BUILD SUCCESSFUL (total time: 0 seconds)

Ahora vemos que en realidad la segunda prueba no debió haber fallado ya que en java la división por cero causa esa ArithmeticException. Para decirle a jUnit que realmente esa excepción no es mala sino que debería considerarse como resultado exitoso de la prueba, lo hacemos a través de el annotation @Test, el método quedará de la siguiente forma:

@Test(expected=ArithmeticException.class)
public void pruebaUno() {
new Matematica().dividir(4, 0);
}

Y al ejecutar la prueba vemos lo siguiente:

Testsuite: PruebaMate
Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0,049 sec

test:
BUILD SUCCESSFUL (total time: 0 seconds)

Nota: En caso que nuestra excepción necesitara ser declarada, agregamos la cláusula throws a nuestro método de prueba.

Ahora posiblemente antes de cada prueba necesitemos realizar un setup del entorno para que se adapte al entorno de producción, esto lo realizaremos con métodos anotados con @After y @Before, por ejemplo realizaremos la instanciación de la clase matemática antes de cada prueba y sugeriremos la recolección de basura después. La clase de pruebas modificada se ve de la siguiente forma:

import org.junit.After;
import org.junit.Test;
import org.junit.Assert;
import org.junit.Before;

public class PruebaMate {

@Test
public void pruebaCero() {
int valor = m.dividir(4, 2);
Assert.assertEquals(2, valor);
}

@Test(expected=ArithmeticException.class)
public void pruebaUno() {
m.dividir(4, 0);
}

private Matematica m;

@Before
public void iniciar() {
System.out.println("Inicio la prueba");
m = new Matematica();
}

@After
public void terminar() {
System.out.println("Termino la prueba");
m = null;
System.gc();
}
}


Y al ejecutar la prueba vemos el resultado:


Testsuite: PruebaMate
Inicio la prueba
Termino la prueba
Inicio la prueba
Termino la prueba
Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0,1 sec

------------- Standard Output ---------------
Inicio la prueba
Termino la prueba
Inicio la prueba
Termino la prueba
------------- ---------------- ---------------
test:
BUILD SUCCESSFUL (total time: 0 seconds)


Ahora, instanciar la misma clase una y otra vez por cada prueba no es barato, como se puede observar en los tiempos de ejecución. Si queremos realizar algo antes de todo y luego algo después de todo utilizamos los annotations @AfterClass y @BeforeClass, modificamos la clase de pruebas de la siguiente forma:

private static Matematica m;

@BeforeClass
public static void iniciar() {
System.out.println("Inicio la prueba");
m = new Matematica();
}

@AfterClass
public static void terminar() {
System.out.println("Termino la prueba");
m = null;
System.gc();
}

Cambiando a static ya que las anotaciones requieren que los métodos sean static. Y los resultados de la ejecución son los siguientes:

Testsuite: PruebaMate
Inicio la prueba
Termino la prueba
Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0,073 sec

------------- Standard Output ---------------
Inicio la prueba
Termino la prueba
------------- ---------------- ---------------
test:
BUILD SUCCESSFUL (total time: 0 seconds)


Con lo que terminamos este tutorial introductorio para realizar pruebas de unidad, las cuales nos ahorran mucho tiempo de introducción de datos y a medida que pasa el tiempo y las pruebas se acumulan nos sirven para verificar que los cambios introducidos en el mantenimiento de las aplicaciones, no tengan efectos adversos sobre la funcionalidad existente.

Espero que les sirva. Saludos!

No hay comentarios.:

Publicar un comentario