Pruebas Unitarias con JUnit 4.5
Las pruebas unitarias nos permiten garantizar el correcto funcionamiento de pequeñas piezas de software, lo cual nos guía hacia proceso de integración ágil y eficiente. JUnit es un framework para realizar pruebas unitarias de nuestras clases hechas en Java.
A partir de su versión 4.5, JUnit incluye soporte para configuración con anotaciones y elimina la necesidad de realizar herencia.
Entre las características más destacadas de JUnit, son las siguientes:
- Configuración pre y post prueba.
- Verificación de valores esperados.
- Especificación de tiempo de espera por cada test.
A continuación demostraremos la sencillez de generar pruebas unitarias con JUnit 4.5.
Nuestra clase por probar es un DAO que contiene un método que busca Productos tomando como criterio de selección la Categoría de los mismos.
package org.yaxche.junit;
public class ProductDao {
private DataAccess dataAccess;
public ProductDao(DataAccess aoDataAccess) {
this.dataAccess = aoDataAccess;
}
public List<Product> findByCategory(Category aoCat) {
...
}
}
La clase que implementa la prueba unitaria para org.yaxche.junit.ProductDao es la siguiente:
package org.yaxche.junit;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
public class ProductDaoTest {
private DataAccess datasource;
private Category category;
public ProductDao() {
}
@BeforeClass
public static void setUpClass() throws Exception {
//configuracion del accesso a datos
datasource = new DataAccess();
...
}
@AfterClass
public static void tearDownClass() throws Exception {
datasource.close();
...
}
@Before
public void setUp() {
category = new Category("music");
}
@After
public void tearDown() {
category = null;
...
}
@Test
public void testFindByCategory() {
ProductDao prodDao = new ProductDao(datasource);
List<Product> prodList = (List<Product>) prodDao.findByCategory(category);
assertNotNull(prodList);
}
}
Al ejecutar la clase de prueba se realizará una búsqueda de productos para la categoría "music" y el juego de resultados será almacenado en un java.util.List la cual será verificada a través de método assertNotNul. Si el resultado es igual a null la prueba será errónea en caso contrario será exitosa.
Ahora análicemos algunos detalles de la implementación del test.
@Test
public void testFindByCategory() {
ProductDao prodDao = new ProductDao(datasource);
List<Product> prodList = (List<Product>) prodDao.findByCategory(category);
assertNotNull(prodList);
}
Al anotar un método con org.junit.Test le indicamos al motor de ejecución de JUnit que se trata de una prueba unitaria, por lo tanto ese método será ejecutado. A la anotación @Test, se le puede indicar el tiempo de espera (timeout) por una respuesta del método en milisegundos.
@Test(timeout=15000)
public void testFindByCategory() {
ProductDao prodDao = new ProductDao(datasource);
List<Product> prodList = (List<Product>) prodDao.findByCategory(category);
assertNotNull(prodList);
}
Con esta modificación el motor de ejecución de JUnit esperará 15 segundos después de ejecutar el método si en ese lapso no obtiene una respuesta la prueba se dará por fallida. También tenemos el parámetro "expected" con el cual indicamos que nuestro método puede generar una excepción.
@Test(expected=DataAccessException.class)
public void testFindByCategory() throws Exception {
ProductDao prodDao = new ProductDao(datasource);
List<Product> prodList = (List<Product>) prodDao.findByCategory(category);
assertNotNull(prodList);
}
En este caso si dentro de la ejecución del test se genera una "DataAccessException", el motor de ejecución de JUnit la manejará y tomara la prueba como válida.
@Before
public void setUp() {
category = new Category("music");
}
La anotación org.junit.Before se utiliza para indicar que el método se debe invocar previo a la ejecución de cada prueba. Es util para preparar datos que deben ser reiniciados en cada prueba. En este caso se crea una instancia de la clase org.yaxche.junit.Category que servira como criterio para la selección de productos.
@BeforeClass
public static void setUpClass() throws Exception {
//configuracion del accesso a datos
datasource = new DataAccess();
...
}
La anotación org.junit.BeforeClass se utiliza para indicar que el método se debe invocar previo a la ejecución de las pruebas y antes de los métodos anotados con org.junit.Before. Es util para preparar datos que deben ser iniciados ants de iniciar todas las pruebas. En este caso se crea una instancia de la clase org.yaxche.junit.DataAccess que sirve para accesar a la capa de persistencia.
@After
public void tearDown() {
category = null;
...
}
La anotación org.junit.After se utiliza para indicar que el método se debe invocar posterior a la ejecución de cada prueba. Es util para finalizar los datos que se reinician en cada prueba.
@AfterClass
public static void tearDownClass() throws Exception {
datasource.close();
...
}
La anotación org.junit.AfterClass se utiliza para indicar que el método se debe invocar posterior a la ejecución de todas las pruebas. Es util para finalizar los datos que se inician antes de la ejecución de todas las pruebas. En esta caso se utiliza para cerrar el acceso a datos.
Una parte fundamental de cada prueba es la verificación de los valores esperados, para lo cual JUnit provee la clase org.junit.Assert, la cual contiene una serie de métodos estáticos orientados a verificar distintos tipos de valores. En nuestro ejemplo utilizamos assertNotNull(Object) el cual simplemente verifica que el objeto no sea nulo, en cuyo caso la prueba será fallida.
Otros métodos disponibles de la clase org.junit.Assert son:
- fail([String]). Ocasiona que la prueba se concluya de manera fallida y envía el mensaje especificado.
- assertTrue(boolean). Verifica que el booleano especificado sea true.
- assertEquals([String], Object, Object). Verifica que el objeto esperado y el objeto generado sean iguales.
- assertNull([String], Object). Verifica que el objeto especificado se null.
- assertSame([String], Object, Object). Verifica que las variables esperada y generada apunten al mismo objeto.
Las pruebas unitarias son una poderosa herramienta en el desarrollo de software de calidad; si bien por sí solas no garantizan eliminar todos los posibles errores en nuestros sistemas, en conjunto con otras pruebas como las integrales, las pruebas de aceptación de usuario y las pruebas de rendimiento, nos encaminan a lograr que nuestros procesos de puesta en producción, sean mucho más controlados. JUnit es una excelente herramienta para realizar pruebas unitarias y a partir de su versión 4.5 es mucho más sencillo de utilizar.
