Introduccrión a JUnit
sábado, septiembre 1, 2001 at 2:00AM Introducción a JUnit
Fecha de creación: 01.09.2001
Revisión 1.0 (01.09.2001)
DySs Ashter
dyss AT java DOT zzn DOT com
|
Cualquier codigo que escribamos deberia ser testeado. No podemos dar por finalizada la implementacion de una clase sin estar seguros de su buen funcionamiento. Esto implica que el codigo no tenga errores de programacion y que ademas realice exactamente lo que nosotros esperamos de el. La unica forma es hacer una serie de test que verifique el codigo que acabamos de escribir. Seguramente todo el mundo testea de alguna manera o otra el codigo. ¿Quien no ha metido System.out.println en el codigo para visualizar estados de variables y asi comprobar que todo ha ido bien hasta ese punto? Pero existen formas mas avanzadas, eficientes y seguras de probar el codigo.
JUnit nos proporciona una forma sencilla, rapida y elegante de escribir test y validarlos de forma automatica. A continuacion os explicare que nos ofrece JUnit.
En la página de JUnit[1] podemos encontrar la ultima version de junit, actualmente la 3.7. Para poder usar Junit lo unico que debemos hacer es incluir el fichero junit.jar en nuestro classpath.
La forma de funcionar tipica de un test con JUnit es: definir unos datos, darles algun tipo de tratamiento, obtener unos resultados y comprobarlos con los resultados que nosotros estabamos esperando. La idea final es que sea el propio JUnit el que no diga si el test ha ido bien o por el contrario el resultado no coincide con el esperado.
Veamos como hacerlo.
Empezaremos viendo como se pueden hacer las comprobaciones de los resultados. La clase junit.framework.Assert, nos proporciona una serie de metodos que son los encargados de hacer dichar comprobaciones. Esta clase contiene una serie de metodos que empiezan por assert. Tenemos metodos assert para comprobar si dos objetos son iguales (assertEquals...), para comprobar si un objeto es nulo (assertNull...), para comprobar si un objeto no es nulo (assertNotNull...) o para comprobar si dos variables apuntan a un mismo objeto (assertSame...).
Cada llamada a un metodo assert podemos considerarla como un test. La logica de estos metodos es simple, si la condicion del metodo assert se cumple el test es valido, en caso contrario el test no es valido y lanza un FailedAssertionError, o sea, un error en el testeo.
Debemos tener cuidado con los bloques try-catch, ya que estos bloques normalmente capturan excepciones que lanza nuestro codigo indicando alguna anomalia. Si en los test usamos algun bloque de este tipo no deberiamos dejar que el test acabe bien. Para este proposito disponemos del metodo fail() o fail(String mensaje), que lanza un FailedAssetionError, y nos informe de la anomalia.
La clase abstracta junit.framework.TestCase es la base sobre la cual definiremos nuestros tests. Esta clase extiende de junit.framework.Assert.
Para crear un test con JUnit debemos seguir estos tres pasos:
Crear una clase que herede de la clase TestCase
public class SimpleTest extends TestCase
Crear un constructor con un parametro de tipo String y llamar al constructor de la clase padre pasandole dicho String.
public SimpleTest( String nombre ) {
super( nombre )
}Crear uno o varios metodos que empiecen por "test", sin parametros y que no devuelvan nada.
public void testSimple() {
...
}
De esta forma tan simple hemos creado una clase de test. Ahora solo nos queda implementar el metodo testSimple().
import junit.framework.TestCase;
public class SimpleTest extends TestCase {
public SimpleTest( String nombre ) {
super( nombre );
}
public static void main( String args[] ) {
junit.textui.TestRunner.run( SimpleTest.class );
}
public void testSimple() {
String s1 = "DySs";
String s2 = "DySs";
assertEquals( s1, s2 );
}
}
Como hemos visto un TestCase no es mas que una clase que contiene uno o varios metodos test...() donde aparecen una o varias llamadas a metodos assert...() o fail().
La ejecucion de un test puede acabar de cuatro formas:
- Con la validacion correcta de todos los asserts. Esto es lo que esperamos.
- Lanzando uno a varios FailedAssertionError con su informacion asociada. Esto ocurre si algun assert no ha sido valido, o sea, la ejecucion de nuestro codigo no ha devuelto el resultado esperado.
- Lanzando cualquier Exception de Java. Esto no es probocado por la validacion de los metodos assert, sino que nos indica algun error de implementacion, ya puede ser del propio TestCase o de nuestro codigo.
- Puede que el resultado sea una mezcla de excepciones de Java y FailedAssertionErrors.
Ejemplo:
import junit.framework.TestCase;
public class ErrorEnTryCatch extends TestCase {
public static void main( String args[] ) {
junit.textui.TestRunner.run( ErrorEnTryCatch.class );
}
public ErrorEnTryCatch( String nombre ) {
super( nombre );
}
public void testTryCatch() {
String error = null;
try {
System.out.println( error.toString() );
} catch( NullPointerException npex ) {
fail( "\nExcepcion capturada por el test\n" +
npex );
}
}
}
La secuencia de ejecucion de una clase que herede de TestCase es la siguiente:
- setUp()
- testAlgo()
- tearDown()
Esta secuencia se repite para todos los metodos test que contenga nuestra clase.
Si tuviesemos varios metodos test que realizan algun tratamiento sobre unos mismos datos, deberiamos usar setUp() para la inicializacion de dichos datos. En setUp() inicializaremos variables, estableceremos conexiones con bases de datos, etc. En tearDown() liberaremos recursos tales como las conexiones a bases de datos.
import junit.framework.TestCase;
public class SimpleTest2 extends TestCase {
private String s1;
private String s2;
private String s3;
public SimpleTest2( String nombre ) {
super( nombre );
}
public static void main( String args[] ) {
junit.textui.TestRunner.run( SimpleTest2.class );
}
public void setUp() {
s1 = "DySs";
s2 = "DySs";
s3 = "Ashther";
}
public void testComparaStrings() {
assertEquals( s1, s2 );
}
public void testConcatenaStrings() {
String test1 = s1 + s3;
String test2 = s2 + s3;
assertEquals( test1, test2 );
}
}
Es posible que tengamos nuestros tests estructurados en varias clases TestCase y nos interese ejecutarlos todos de una sola vez. Con este proposito JUnit nos proporciona la clase TestSuite que sirve para agrupar tanto TestCases como otras TestSuite.
Para crear una coleccion de tests debemos seguir los siguientes pasos:
Crear una clase que herede de TestCase
public class VariosTest extend TestCase
Crear un constructor con un parametro de tipo String y llamar al constructor de la clase padre pasandole dicho String.
public VariosTest( String nombre ) {
super( nombre )
}Crear un metodo estatico de nombre suite, sin parametros de entrada y que devuelva un Test.
public static Test suite()
Crear un TestSuite dentro del metodo suite
TestSuite suite = new TestSuite();
Añadir tests al TestSuite
// Solo un metodo test
suite.addTest( new SimpleTest2( "testConcatenaString" );
// Todos los metodos test de un TestCase
suite.addTestSuite( SimpleTest.class );
// Otro TestSuite
suite.addTest( MiTestSuite.suite() );devolver el TestSuite
return suite;
Ejemplo:
import junit.framework.TestSuite;
import junit.framework.Test;
import junit.framework.TestCase;
public class VariosTest extend TestCase{
public VariosTest( String nombre ) {
super( nombre );
}
public static void main( String args[] ) {
junit.textui.TestRunner.run( suite() );
}
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite( SimpleTest.class );
suite.addTest( new SimpleTest2( "testConcatenaStrings" ) );
return suite;
}
}
Hasta ahora hemos visto como crear nuestros test, ahora veremos la forma de ejecutarlos. Todos los ejemplos tienen un metodo main donde se llama a junit.textui.TestRunner. TestRunner es la clase encargada de ejecutar nuestros tests. Disponemos de tres clases TestRunner, una en modo testo y dos mas en modo grafico, Swing y AWT.
public class VisualizaResultados {
public static void main( String args[] ) {
// Modo texto
junit.textui.TestRunner.run( SimpleTest2.class );
// Modo grafico con AWT
junit.awtui.TestRunner.run( SimpleTest2.class );
// Modo grafico con Swing
junit.swingui.TestRunner.run( SimpleTest2.class );
}
}
Conclusión
La forma ideal de usar JUnit es desarrollando en paralelo el codigo de la clase y el test JUnit para ese trozo de codigo, en vez de escribir toda la clase y luego escribir el test. Si desarrollamos en paralelo siempre nos sera mas facil depurar el codigo y testearlo mucho mejor, pero como he dicho es una forma de trabajar, no una obligacion.
Como hemos podido comprobar el uso de JUnit es muy sencillo y a la vez potente. Y esto es solo la punta de iceberg, podeis encontrar mucha mas informacion en su pagina oficial, www.junit.org, eso si, esta en inglés.
Recursos
[1] Web de JUnit.,
http://www.junit.org
Acerca del autor
DySs Ashter
DySs Ashter, desarrollador de EJBs en Barcelona dentro del desarrollo de una plataforma aseguradora en lo que es su primer trabajo como desarrollador JAVA.
j2se 