2. ENTORNOS DE PRUEBA I
¿Qué son?:
La mayoría de la gente que escribe programas tiene como
mínimo algo de experiencia con unidades de prueba.
Si alguna vez has escrito unas pocas líneas de código para
probar que lo que has programado es correcto, habrás
construido una.
Por otra parte,las grandes aplicaciones tienen un conjunto
muy extenso y variado de casos de prueba que son
ejecutados repetidamente y se añaden a la salida del
proceso de desarrollo. La unidad de prueba es útil en todos
los niveles de programación.
3. ENTORNOS DE PRUEBA II
La relación entre las unidades de prueba con el
código de una aplicación se constituye mediante la
comunicación usando métodos:
4. ENTORNOS DE PRUEBA
III:Características - ¿Para qué sirven?
Pueden ser usados para comprobar muchas
arquitecturas complejas.
Es mucho más fácil que desarrollar test independientes y
además producen pruebas efectivas y fiables:permiten un
desarrollo rápido de nuestra aplicación.
2 tipos :dependiendo de la cantidad de accesos a los
procesos internos que puedan estar siendo probados
– cajas negras : funcional
● ejecuta un programa y comprueba que devuelve un código
determinado
– o cajas blancas: estructurales
● acceden a la estructura del código que está siendo probado
5. ENTORNOS DE PRUEBA
IV:Conclusiones
Usar entornos de prueba obliga a realizar una buena
práctica del desarrollo orientado a objetos ya que están
escritas para probar sólamente las partes públicas de los
objetos a testar. Esto cubre el diseño de los objetos con
discrección, interfaces de prueba y un mínimo de
comportamiento ocultado complejo. La mayoría de los
lenguajes orientados a objetos nos brindan protección de
acceso, no permitiendo el acceso externo desde otras
clases a partes protegidas o privadas.
Los desarrolladores escriben patrones de programación
así, diseñan y construyen código. Estas pruebas suelen
probar elementos de código de bajo nivel, así como
métodos e interfaces.
6. Pruebas para el desarrollo conducido
TDD: Test Driven Development, también conocidas como primeras
pruebas de programación son una de las más significantes y completas
prácticas utilizadas como programación extrema (XP:Extreme
Programming) y otras metodologías de Desarrollo Ágil. Los entornos de
prueba consiguen su máxima utilidad cuando son usados para habilitar
estos TDD,aunque siguen siendo útiles cuando TDD no se sigue.
La regla dominante de TDD puede ser resumida como la de "prueba dos
veces, programa una vez," por analogía a la regla del carpintero:"mide
dos veces, corta una vez."; refiriéndose al proceso de 3 pasos que tiene
que ver con cualquier cambio en el código:
– 1.Escribir una prueba para el nuevo código y comprobar si falla.
– 2.Escribir el nuevo código , haciendo "las cosas más simples que
posiblemente podrían funcionar".
– 3.Comprobar que se completa correctamente la prueba, y replanteamos el
código.
7. Éstos tres básicos pasos son el ciclo TDD
El paso 1 es escribir la prueba , ejecutarla y comprobar los
posibles fallos. Los fallos son importantes porque nos sirven para
validar que la prueba ha fallado como se esperaba.Suele ser una
tentación el saltarse la ejecución y comprobar que realmente falla.
No debe de hacer esto.
En el paso 2 el código es escrito para hacer que el test sea
superado.Una sabia pauta es hacer "lo más simple que
posiblemente podría funcionar."
En el paso 3, la prueba tiene éxito, verificando el nuevo código y su
prueba. En este punto, el nuevo código puede ser replanteado de
nuevo,éste es un concepto de la ingeniería de software definido
como "comportamiento-preservando la transformación." Más
formalmente, el replanteamiento (refactoring) es el proceso de
transformar código para mejorar su diseño interno sin cambiar su
funcionalidad externa.
8. Conclusiones de TDD I
Las unidades de prueba tienen valor en cuanto a la
capacidad de demostrar que la nueva funcionalidad
añadida a un programa realmente funciona. Escribir la
prueba fuerza a pensar por adelantado en el diseño ideal
del nuevo código. Así, de una manera disimulada y sutil,
TDD hace toda la nueva pieza del desarrollo con un
proceso metódico, bajo el diseño de software.
Una vez que la nuevas pruebas y funcionalidades de la
unidad estén en su lugar, la prueba de unidad sirve como
el ejemplo definitivo de funcionamiento para comprobar
cómo el nuevo código se supone que va a ser utilizado.
Por estas razones, el tiempo que se perdió escribiendo
pruebas de unidad no está gastando sólamente esfuerzo.
Las inversiones en las pruebas son proporcionales al
diseño.
9. Conclusiones de TDD II
Al eliminar errores,depurando, se debe escribir primero una
prueba de unidad que falle debido al error esperado. Esto es un
esfuerzo útil en sí mismo, porque se determina exactamente cómo
ocurre éste error. Una vez que la prueba de unidad está en lugar y
falle,hay que fijar el error y volver a efectuar la prueba para
verificar que el bug está solucionado.
Aparte de fijar el error, este proceso tiene la ventaja adicional de
crear una prueba que lo captrue. Si el bug se reintroduce siempre,
la prueba fallará y el problema persistirá. Siguiendo el ciclo de
TDD, se puede escribir código sin errores como sea humanamente
posible en un primer intento, en otras palabras ,escribir de una
sóla vez. El proceso da una indicación clara de que una parte del
trabajo está hecho. Cuando una nueva unidad de prueba se escribe
y después falla, la tarea está a medio camino de ser completada. No
se puede mover a ningún otro sitio hasta que las pruebas sean
superadas.
10. La Familia xUnit
Kent Beck publicó un entorno de unidad de pruebas
para el lenguaje Smalltalk en 1999. La arquitectura de
esta unidad (SUnit) representa un equilibrio ideal entre
simplicidad y utilidad. Más tarde, Erich Gamma portó
SUnit a Java, creando JUnit, que se volvió a convertir en
CppUnit,NUnit,PyUnit, XMLUnit, y conversiones a
muchos otros lenguajes.
Un impresionante conjunto de entornos de unidad de
prueba fueron construidos con el mismo modelo actual.
Estos entornos se conocen como la familia de
herramientas xUnit. Todo es software libre,de fuente
abierta.
11. Miembros de la familia xUnit
Algunos de los más populares entornos de prueba xUnit junto con
los lenguajes a los que están orientados y el dominio de las
pruebas:
– JUnit :La referencia de la implementación de xUnit, es en gran
medida el entorno más usado y extendido de unidad de prueba.Está
implementada dentro y usada desde Java.
– CppUnit :Conversión a C++ desde JUnit, sigue muy de cerca el
modelo de JUnit.
– NUnit :Más bien que una conversión directa de JUnit, tiene una
puesta en práctica específica de .NET, que sigue generalmente el
modelo de xUnit. Se escribe en C # y puede ser utilizado probar
cualquier lenguaje de .NET, incluyendo C #, VB.Net, J #, y C++
manejado.
– PyUnit :La versión Python de xUnit. Está incluida como un
componente estándar de Python 2.1.
12. Miembros de la familia xUnit II
SUnit :También conocida como SmalltalkUnit, es la xUnit
original, y las bases de la arquitectura de xUnit architecture.
Está escrita en lenguaje Smalltalk y se usa desde éste.
vbUnit:Es la xUnit para Visual Basic (VB). Está escrita en
este lenguaje y soporta la construcción de pruebas en VB asó
como desarrollo de COM (componentes).
utPLSQL :Es la xUnit para el lenguaje PL/SQL de Oracle.
Está escrita en PL/SQL y se usa desde éste lenguaje.
MinUnit : Un gran ejemplo de un pequeño pero funcional
entorno de unidades de prueba. Está implementado en tres
líneas de C pero se usa para probar código en éste lenguaje.
13. JUnit
El Entorno de unidades de prueba JUNIT es la implementación de
xUnit. Como su nombre dice, ha sido desarrollada y usada con
Java. Es el mas usado y analizado entorno de unidades de pruebas
de software hoy por hoy.JUnit es la base de utilidades de prueba
mas específicas, como Cactus, Jester, JunitPerf, y muchos mas e
integrado con otros como Ant.
El objeto de JUnit es proporcionar un entorno para construir y
ejecutar unidades de pruebas. La distribución JUnit también es un
buen ejemplo de un simple y sólido producto software que ha sido
creado usando desarrollo guiado con pruebas. Es importante
reseñar que el código de JUnit es muy instructivo.
JUnit es software abierto (opensource) y podemos obtener su
código desde www.junit.org , siendo la última versión la 3.8.1.
14. Arquitectura de JUnit
JUnit contiene 75 clases concretas y una serie de clases internas e
interfaces. Está organizado en paquetes como se muestra a
continuación
15. Arquitectura del paquete junit.framework
La arquitectura de junit.framework sigue el
modelo de la arquitectura xUnit. En particular
nos muestra el elemento Test, que está
implementado por las clases TestCase y TestSuite.
La clase abstracta TestCase es el padre de todas
las clases de unidades de prueba.
Las clases principales usadas cuando se
construyen las unidades de prueba son TestCase,
TestSuite y TestRunner.
17. Uso de JUnit
Las clases de prueba en JUnit son subclases de TestCase. Las pruebas pueden
ser construidas de muchas formas, pero el mejor enfoque es el siguiente:
El nombre de la clase de prueba comienza con el nombre del objeto que está
siendo testeado y finalizarlo con “Test”. Las clases Test contienen un método
separado para cada comportamiento a ser probado.
Las condiciones de prueba son comprobadas con métodos de comprobación. Los
resultados obtenidos en las pruebas de comprobación son “éxito” o “fallo”. Si la
comprobación falla, el método de prueba devuelve el control inmediatamente. Si
la comprobación es satisfactoria, la ejecución de los métodos de prueba
continuan. Cómo un método de prueba solamente comprueba un
comportamiento simple, en la mayoría de los casos cada método de prueba
debería contener solamente un test de comprobación.
Si hay objetos que son compartidos por los métodos de prueba, los objetos deben
ser inicializados en el método setUp() y destruidos en el método tearDown().
Estos métodos son llamados antes y después de cada llamada a pruebas de
método.
18. Ejemplo de uso de JUnit
PruebaLibreria.javaimport junit.framework.*;
import java.util.*;
public class PruebaLibreria extends TestCase {
private Libreria liberia;
public void setUp( ) throws Exception {
libreria = new Libreria( );
libreria.insertarLibro(new Libro( "Cosmos", "Carl Sagan" ));
libreria.insertarLibro(new Libro( "Contact", "Carl Sagan" ));
libreria.insertarLibro(new Libro( "Solaris", "Stanislaw Lem" ));
libreria.insertarLibro(new Libro( "American Beauty", "Allen M
Steele" ));
libreria.insertarLibro(new Libro( "American Beauty", "Edna
Ferber" ));
}
//….
19. Ejemplo de uso de JUnit II
public void tearDown ( ) {
libreria.empty( );
libreria = null;
}
public void testGetLibrosPorTitulo ( ) {
Vector Libros = libreria.getLibrosPorTitulo( "American Beauty" );
assertEquals( "se encontró un número incorrecto de Libros", 2, Libros.size( ) );
}
public void testGetLibrosPorTitulo( ) {
Vector libros = libreria.getLibrosPorTitulo( "Carl Sagan" );
assertEquals( "2 libros no encontrados", 2, libros.size( ) );
}
public void testVacio( ) {
libreria.empty( );
assertEquals( "libreria no está vacía", 0, libreria.getNumLibros( ) );
}
}
Como podemos observar en LibreriaTest realizamos pruebas, creando una
instancia de Libreria. El objeto Libreria es creado e inicializado con una serie de
Libros en la función setUp(); su contenido interno es borrado posteriormente en
tearDown().
20. Ejecutando JUnit
Cada método de prueba verifica un comportamiento
diferente de la clase Libreria con un solo test. Aunque los
test pueden modificar el objeto Libreria, (como puede
verse en el método testEmpty(), el test garantiza que cada
prueba se ejecuta en un entorno libre, sin dependencias
de los resultados de otras.
Los test son ejecutados usando una de las utilidades
TestRunner suministradas con JUnit. El mas simple y
mas fácil de estos es TextTestRunner:
Ejemplo de uso de TextTestRunner ejecutando
LibreriaTest:
$ java junit.textui.TestRunner PruebaLibreria.........Time:
0.01 OK (3 tests)
21. Ejecutando JUnit : varios tests
Esto a veces es interesante para añadir múltiples test que pueden
ejecutarse juntos. La clase TestSuite se usa para contener una
colección de test.
El ejemplo siguiente muestra una clase LibreriaTest la cual crea un
TestSuite conteniendo un número de clases de prueba. El método
estático suite() crea y devuelve el TestSuite:
PruebasLibreria.javaimport junit.framework.*;
public class PruebasLibreria extends TestSuite {
public static Test suite( ) {
TestSuite suite = new TestSuite( );
suite.addTest(new TestSuite(PruebaLibro.class));
suite.addTest(new TestSuite(PruebaLibreria.class));
suite.addTest(new TestSuite(PruebaDBLibreria.class));
suite.addTest(new TestSuite(PruebaFuncLibreria.class));
return suite;
}
}
22. Ejecutando JUnit : resultados
Cuando PruebasLibreria es ejecutado usando
TexTestRunner, todos los métodos de pruebas en
cada clase de pruebas son buscados y ejecutados
como se puede ver a continuación:
$ java junit.textui.TestRunner
PruebasLibreria.................Time: 0.851OK (17
tests)
23. GUI JUnit::TestRunner
La versión Swing de
TestRunner es
llamada por
PruebasLibreria(Libr
ayTests) como se
muestra:
$ java
junit.swingui.TestRunne
r PruebasLibreria
24. Extensiones xUnit
Además de los xUnits, están disponibles
muchas herramientas adicionales que
amplían la funcionalidad de los
entornos existentes de unidad de prueba
, en dominios especializados, actúan
más bien como herramientas
independientes.
25. Extensiones xUnit : tipos más conocidos
XMLUnit :Una extensión xUnit para soportar testeo XML. Las
versiones que existen son para usar con JUnit y NUnit: una
extensión JUnit que soporta escritura de código funcional y
pruebas escalares. Está escrita en Java y se usa desde este
lenguaje.
Cactus :Una extensión JUnit para probar la unidad del código de
servidores como servlets (objetos que corren dentro del contexto
de un servidor de aplicaciones, como Tomcat),
JSPs, o EJBs. Está escrita en Java y se usa con este lenguaje.
JFCUnit :Una extensión JUnit que soporta pruebas gráficas (con
GUI: graphic user interface) para las aplicaciones que usan Swing,
de Java. Está escrita en Java y se usa desde éste.
26. Extensiones xUnit : tipos más conocidos
NUnitForms :Extesnión NUnit que soporta pruebas de GUI de
alpicaciones "Windows Forms". Está escrita en C# y puede ser
usada por cualquier lenguaje de la plataforma .NET.
HTMLUnit : Una extensión para JUnit que prueba aplicaciones
basadas en la web.Simula un navegador y está orientado a escribir
pruebas que usan páginas en HTML.
HTTPUnit :Otra extensión JUnit que prueba aplicaciones basadas
en la web.Orientada a escribir pruebas que tratan con objetos de
peticiones y respuestas HTTP.
Jester :Una "provechosa" extensión que encuentra
automáticamente el código que no se cubre por las unidades de
prueba, mostrándolo.Existen versiones para Python (Pester) y
NUnit (Nester). Existen muchas otras herramientas de cobertura
de código con funcionalidades similares.
27. La Arquitectura xUnit
Todas las xUnits tienen la misma arquitectura
básica. Ahora describiremos los fundamentos de
xUnit, usando JUnit como referencia de ejemplo,
hasta el momento es la más usada de las xUnits.
La otras xUnits varían en sus detalles de
implementación,pero siguen el mismo patrón y
generalmente contienen el mismo conjunto clave
de clases y conceptos. Las clases de "alto nivel"
(key-class: sin constructor,de las que podemos
usar sus métodos y propiedades) son TestCase,
TestRunner, TestFixture, TestSuite, y TestResult.
28. xUnit::TestCase
La clase más elemental de xUnit, es la base de una
unidad de prueba
Es una clase abstracta,
el padre de todas las unidades
de prueba de xUnit.
Todas las unidades de prueba heredan de esta clase,
para crear una unidad de pruebase define una clase
prueba que es un descenciente de la clase elemental a
la que se le añade un método prueba.
29. Ejemplo: PruebaLibro.java,
una clase basada en TestCase
PruebaLibro.java
import junit.framework.*;
public class PruebaLibro extends TestCase {
public void testConstructLibro( ) {
Libro libro = new Libro("El Alquimista");
assertTrue( libro.getTitulo( ).equals("El
Alquimista") );
}
}
30. Ejemplo. PruebaLibro,una clase basada en
TestCase
El método testConstructLibro() usas assertTrue() para verificar el
valor del título del libro. Las condiciones de prueba son siempre
evaluadas por los métodos "assert" ( asegurar ) del entorno de
pruebas. Si una condición se evalúa como TRUE , el entorno
incrementa el contador de pruebas superadas, en otro caso, si es
FALSE, ocurre un fallo en la prueba y el entorno almacena los
detalles, incluyendo la posición del error en el código.Después de
un fallo, el entorno se salta el resto de las líneas de código para ese
método,ya que el resultado de la prueba ya se sabe.
PruebaLibros prueba la clase Libro:Libro.java
public class Libro {
private String titulo = "";
Libro(String titulo) { this.titulo = titulo; }
String getTitulo( ) { return titulo; }
}
31. Ejecutando el ejemplo de TestCase
PruebaLibro puede ser ejecutada añadiendo una función main()
que llame al método de prueba del constructor:PruebaLibro.java
import junit.framework.*;
public class PruebaLibro extends TestCase {
public void testConstructLibro( ) {
Libro libro = new Libro("El Alquimista");
assertTrue( libro.getTitulo( ).equals("El Alquimista") );
}
public static void main(String args[]) {
PruebaLibro prueba = new PruebaLibro( );
prueba.testConstructLibro( );
}
}
32. Ejecutando el ejemplo de TestCase II
Compilando y ejecutando PruebaLibro produce una carencia en la salida bastante
decepcionante que no da demasiada confianza puesto que podría haber sucedido
cualquier cosa realmente.
> javac PruebaLibro.java
> java PruebaLibro
(Para que los comandos funcionen como se muestra, junit.jar y el directorio que contiene las
clases de pruebas deben de estar en la variable de entorno classpath de Java . )
Los resultados serían más interesantes si PruebaLibro estuviera hecho para fallar , por
ejemplo,cambiando la condición de assertTrue() por FALSE.
> java PruebaLibro
Exception in thread "main" junit.framework.AssertionFailedError
at junit.framework.Assert.fail(Assert.java:47)
at junit.framework.Assert.assertTrue(Assert.java:20)
at junit.framework.Assert.assertTrue(Assert.java:27)
at PruebaLibro.testConstructLibro(PruebaLibro.java:7)
at PruebaLibro.main(PruebaLibro.java:12)
Ahora podemos comprobar que la unidad de prueba del entorno está haciendo su
trabajo, ejecutando la prueba y obteniendo como resultado el fallo de la misma. Esto
demuestra que un entorno xUnit puede ser usado de una forma muy simple y directa.
33. xUnit:: TestRunner I
Las unidades de prueba básicas pueden ser construidas
en TestCase sin ningún conocimiento adicional del
entorno. Sin embargo, xUnit tiene muchas otras
funcionalidades que ofrecer. Una de las piezas más
valiosas es TestRunner.
Una clase TestRunner devuelve detalles acerca de los
resultados de la prueba y la simplifca. Es un objeto
bastante complejo que en JUnit, viene en tres "sabores":
AWT TestRunner, Swing TestRunner y TestRunner
textual (llamado también TextTestRunner) El propósito
de estas clases es ejecutar una o más clases TestCases y
devolver los resultados.
35. xUnit :: TestRunner II
Los métodos más importantes de TextTestRunner son run( ), al que se le pasa
una prueba para ejecutar y main( ), que hace de TextTestRunner una clase
ejecutable, y se ejecutará con la clase de prueba PruebaLibro como su
argumento. Encontrará el método de prueba testConstructLibro y lo ejecutará.
Podemos eliminar el método main( ) de la clase PruebaLibro, ya que no la
vamos a usar para ejecutar la prueba.
Código replanteado de la clase PruebaLibro, hecha de una forma más simple
PruebaLibro.java
import junit.framework.*;
public class PruebaLibro extends TestCase {
public void testConstructLibro( ) {
Libro libro = new Libro("El Alquimista");
assertTrue( libro.getTitle( ).equals("El Alquimista") );
}
}
PruebaLibro se reduce hasta lo esencial. Ahora, usamos TextTestRunner para
ejecutarla:
36. xUnit :: TestRunner III
Usando TestRunner no sólo se elimina código innecesario de PruebaLibro sino que además provee
un resultado mejor configurado de cómo muchas de las pruebas fueron ejecutadas y cuánto tiempo
se tomó en hacerlo.
Las clases Test (de pruebas) suelen tener varios métodos de prueba, TestRunner las encontrará
todas y las ejecutará,para ello se usa "test" para empezar el nombre del método.
PruebaLibro con un segundo método añadido. La nueva prueba comprueba el autor del libro:
PruebaLibro.java
import junit.framework.*;
public class PruebaLibro extends TestCase {
public void testConstructLibro( ) {
Libro libro = new Libro("El Alquimista", "");
assertTrue( libro.getTitulo( ).equals("El Alquimista") );
}
public void testAutor( ) {
Libro libro = new Libro("El Alquimista", "Paulo Coelho");
assertTrue( libro.getAutor( ).equals("Paulo Coelho") );
}
}
37. xUnit :: TestRunner IV
El atributo autor y su función de acceso getAutor() han de ser añadidos a la clase Libro:
Libro.java
public class Libro {
private String titulo = "";
private String autor = "";
//Constructor con 2 parámetros
Libro(String titulo, String autor) {
this.titulo = titulo;
this.autor = autor;
}
public String getTitulo( ) { return titulo; }
public String getAutor( ) { return autor; }
}
– Ejecutando PruebaLibro se muestra que el entorno está corriendo dos pruebas:
> java junit.textui.TestRunner PruebaLibro
..
Time: 0.01
OK (2 tests)
38. xUnit :: TestRunner : resultados
Se imprime un punto cuando cada prueba es ejecutada,
como un indicador de progreso. La salida de la prueba
concluye con el número de pruebas y el tiempo utilizado.
Muchos de las xUnits incluyen una interfaz gráfica (GUI
TestRunner) para ofrecer un entorno visual con
objetos.Los resultados son mostrados en verde si todas
las pruebas funcionaron bien , o en rojo si no lo hicieron.
Esto es el origen de las barras verdes y rojas. El ciclo
TDD a veces se describe como "Replanteamiento Rojo-
Verde" por esto. Primero, se implementa una nueva
prueba que falla, provocando una barra roja; después se
hace el cambio más fácil posible que rectifique la barra
en una verde; por último, se hace el replanteamiento del
código "feo" que se introdujo.
39. xUnit:: TestFixture
Para explicar las pruebas de acoplamiento hay que dar otro concepto importante de
xUnit,necesitamos un ejemplo de unidad de prueba más complejo; añadimos la clase
Libreria para permitir que se puedan insertar más de un Libro y para obtener el número
de ellos insertados.
Versión inicial de PruebaLibreria:
PruebaLibreria.java
import junit.framework.*;
import java.util.*;
public class PruebaLibreria extends TestCase {
public void testInsertarLibro( ) {
Libreria libreria = new Libreria( );
libreria.insertarLibro(new Libro("El Alquimista", "Paulo Coelho"));
libreria.insertarLibro(new Libro("Solaris", "Stanislaw Lem"));
Libro libro= libreria.getLibro( "El Alquimista" );
assertTrue( libro.getTitulo( ).equals("El Alquimista") );
libro = libreria.getLibro( "Solaris" );
assertTrue( libro.getTitulo( ).equals("Solaris") );
}
public void testTamLibreria( ) {
Libreria libreria = new Libreria( );
libreria.insertarLibro(new Libro("El Alquimista", "Paulo Coelho"));
libreria.insertarLibro(new Libro("Solaris", "Stanislaw Lem"));
assertTrue( libreria.getNumLibros( ) == 2 );
}
}
40. xUnit:: TestFixture II
Los dos métodos de prueba implementados: testInsertarLibro( ) añade dos Libros a la
Libreria, después usa getLibro( ) para comprobar que la inserción no falló.
testTamLibreria( ) añade también dos Libros y luego comprueba que getNumLibros( )
devuelve "2".
Ahora añadimos las funcionalidades para pasar las pruebas a la clase Libreria:
Libreria.java
import java.util.*;
public class Libreria {
private Vector libros;
Libreria( ) {
libros = new Vector( );
}
public void insertarLibro( Libro libro) {
libros.add( libro );
}
public Libro getLibro( String titulo ) {
for ( int i=0; i < libros.size( ); i++ ) {
Libro libro= (Libro) libros.elementAt( i );
if ( libro.getTitulo( ).equals(titulo) )
return libro;
}
return null;
}
public int getNumLibros( ) {
return libros.size( );
}
}
41. xUnit:: TestFixture III
Libreria usa ahora un Vector que contiene una colección
de Libros. El método getNumLibros( ) devuelve el
número de Libros de la colección. Los métodos
insertarLibro( ) y getLibro( ) añaden y eliminan un
Libro de la colección
Cuando usamos TextTestRunner para ejecutar
PruebaLibreria ambos métodos superan la prueba:
> java junit.textui.TestRunner PruebaLibreria
..
Time: 0.05
OK (2 tests)
42. xUnit:: TestFixture IV
PruebaLibreria tiene unos cuantos problemas... El primero y antes
de nada,la cantidad de código duplicado entre dos métodos de
prueba es "fastidioso" ya que los dos crean una prueba de
Libreria y añaden dos libros a ella. El segundo,otra preocupación
es que pasaría uno de los asserts falla: el resto del código del
método de la prueba no se ejecutaría y ningún objeto es destruido.
En Java, el recolector de basura capturaría los objetos y los
borraría de la memoria automáticamente, pero las unidades de
prueba suelen usar objetos o recursos que deben ser cerrados o
borrados explícitamente.
Una forma de no tener que estar pendiente de la duplicación de
código es hacer que la Libreria de la prueba sea un miembro de la
clase PruebaLibreria y hacer que la primera prueba la inicialice y
añada los dos elementos iniciales. La segunda prueba podría
asumir que aquella funcionó correctamente,entonces se ejecuta y
queda limpia.
43. xUnit:: TestFixture V
Desafortunadamente ,esta solución añade más problemas
potenciales, si la primera prueba falla, la segunda también debe
hacerlo puesto que sus condiciones iniciales son incorrectas,
incluso aunque no hubiese nada mal en su propia prueba.
La segunda prueba siempre fallará a menos que la primera sea
ejecutada antes que ella,así que no pueden ser ejecutadas por
separado o en orden inverso.Además,si cualquiera de las dos
fallara resultaría en que las cosas no terminarían de una forma
limpia(no se llama a los destructores,etc.)
En general, las unidades de prueba bien escritas están aisladas.
Una prueba aislada no depende de ningún modo en los resultados
de otras pruebas. Para asegurar el aislamiento, las pruebas no
deben de compartir objetos que cambien.
44. xUnit:: TestFixture VI
Las pruebas que tienen interdependencias están
emparejadas. En PruebaLibreria, si uno de los métodos
prueba asume que el otro deja la clase Libreria en un
cierto estado, es un claro ejemplo de una prueba con
emparejamiento (en este caso de métodos).
La arquitectura xUnit nos ayuda para asegurarnos de
que las pruebas estén aisladas con pruebas de
acoplamiento, que son un entorno de prueba usado en
muchas pruebas. Están implementadas como TestCase
con múltiples métodos de pruebas que comparten objetos
que representan el entorno de pruebas común.
45. Relación entre TestFixture y TestCase
La clase hija aunque se llame
TextFixture es TestCase,cada una
de ellas es implícitamente una
TestFixture, aunque puede no
actuar como tal. El
comportamiento de TestFixture
viene a ejecutarse cuando varios
métodos prueba tienen objetos en
común. La función setUp() es
llamada antes de cada método de
prueba, estableciendo el entorno
para la prueba.La función
tearDown( ) siempre es llamada
después de cada método de
prueba para limpiar este entorno,
incluso si ha ocurrido un error.
46. Relación entre TestFixture y TestCase II
Así, aunque las pruebas usen los mismos objetos, pueden hacer cambios sin
posibilidad de que afecte a la siguiente función prueba.
El comportamiento de TestFixture crea y destruye efectivamente la clase de
prueba cada vez que son llamados sus métodos prueba. Esto desemboca en que
las pruebas son aisladas.
Algunas xUnits (como CppUnit) tienen una clase real o interface llamada
TestFixture de la que desciende TestCase ,mientras que otras (JUnit) sólo
permiten a TestCase actuar como una clase TestFixture.
Escribir pruebas como TestFixtures tiene una serie de ventajas. Los métodos de
prueba pueden compartir objetos pero siguen ejecutándose de forma aislada; el
emparejamiento de pruebas es minimizado y los métodos de prueba que
comparten código pueden ser agrupados en la misma clase TestFixture. La
duplicación de código entre pruebas se ve reducida. Se garantiza la limpieza en
el código,de forma que será ejecutada tanto si una prueba falla como si no. Por
último , los métodos de prueba pueden ser ejecutados en cualquier orden ,ya que
son aislados.
47. Ejemplo usando xUnit::TestFixture
PruebaLibreria implementada como un TestFixture. En el ejemplo, el entorno
compartido de pruebas acopladas contiene una instancia de Libreria y dos
Libros.
import junit.framework.*;
import java.util.*;
public class PruebaLibreria extends TestCase {
private Libreria libreria;
public void setUp( ) {
libreria = new Libreria( );
libreria.insertarLibro(new Libro("El Alquimista", "Paulo Coelho"));
libreria.insertarLibro(new Libro("Solaris", "Stanislaw Lem"));
}
public void tearDown( ) {
}
public void testGetLibros( ) {
Libro libro = libreria.getLibro( "El Alquimista" );
assertTrue( libro.getTitulo( ).equals( "El Alquimista" ) );
libro = libreria.getLibro( "Solaris" );
assertTrue( libro.getTitulo( ).equals( "Solaris" ) );
}
public void testTamLibreria( ) {
assertTrue( libreria.getNumLibros( ) == 2 );
}
}
48. Ejemplo usando xUnit::TestFixture II
La mejora de estilo con respecto a la versión anterior de PruebaLibreria es
evidente: la duplicación de código ha desaparecido, los métodos de prueba sólo
contienen sentencias específicas relacionadas con las condiciones de las pruebas
y las pruebas son más fáciles de entender.
Nótese que el método de prueba llamado testInsertarLibros() se llama ahora
testGetLibros( ) para describir mejor lo que hace.
Cuando se ejecuta PruebaLibreria, la secuencia de la función es:
setUp( )
testGetLibros( )
tearDown( )
setUp( )
testTamLibreria( )
tearDown( )
Las llamadas a setUp( ) y tearDown( ) inicializan y limpian la prueba de
acoplamiento cada vez que se llama al método, así aisla al resto de pruebas.
49. xUnit:: TestSuite
Hace mucho, la revisión de xUnit se centraba en
escribir clases en una sóla unidad de pruebas,a
veces con multitud de métodos de prueba. ¿Qué
pasó con las pruebas de muchas clases de
unidades de prueba?,después de todo,cada objeto
producido debería tener su unidad de prueba
correspondiente.
xUnit contiene una clase para agregar unidades
de prueba llamada TestSuite,que está relacionada
de cerca con TestCase, ya que ambas son
descendientes de la misma clase abstraacta
50. xUnit:: TestSuite II
La figura muestra la interfaz de la prueba Test y
cómo TestSuite y TestCase la implementan.
51. xUnit:: TestSuite III
TestSuite, TestCase tiene como su padre,la interface
Test,que contiene el método run() y usa el entorno para
ejecutar pruebas y reunir sus resultados. Ya que
TestSuite implementa run(), puede ser ejecutada como
una TestCase. Cuando una TestCase es ejecutada,sus
métodos de prueba también lo son. Cuando una
TestSuite es ejecutada, sus TestCases también.Las clases
TestCases son añadidas a TestSuite usando la función
addTest();debido a que TestSuite es en sí misma una
clase Test, una TestSuite puede contener otras
TestSuites, permitiendo al intrépido desarrollador el
poder construir jerarquías de TestSuites y TestCases.
52. Ejemplo de xUnit::TestSuite
Clase TestSuite derivada : PruebasLibreria que contiene
PruebaLibro y PruebaLibreria:
PruebasLibreria.java
import junit.framework.*;
public class PruebasLibreria extends TestSuite {
public static Test suite( ) {
TestSuite suite = new TestSuite( );
suite.addTest(new TestSuite(PruebaLibro.class));
suite.addTest(new TestSuite(PruebaLibreria.class));
return suite;
}
}
53. Ejemplo de xUnit::TestSuite II
Una instancia de TestSuite es creada y añadida a PruebasLibreria por cada una
de las clases de prueba. Ésta es una forma rápida de añadir todas los pruebas al
conjunto suite de una sóla vez. La función addTest( ) debe ser usada además
para añadir métodos al conjunto de pruebas de forma individual, ejemplo:
– suite.addTest(new PruebaLibreria("testInsertarLibros"));
Para ser usada de esta forma,una instancia de TestCase debe tener un
constructor que tome un argumento de tipo cadena que llame al constructor de
su padre.El argumento especifica el nombre del método de prueba a ejecutar.
Se pueden ejecutar instancias de TestSuite usando una clase TestRunner tal
como si ejecutaramos una TestCase. El método estático de TestSuite ,suite( ) se
llama para crear el conjunto suite de pruebas a ejecutar.
> java junit.textui.TestRunner PruebasLibreria
....
Time: 0.06
OK (4 tests)
Los resultados muestran que ambos métodos de prueba de las clases de las
unidades PruebaLibreria y PruebaLibro han sido ejecutadas, con un total de 4
pruebas.
54. xUnit::TestResult
TestResult es el parámetro para
ejecutar el método run() de la
clase Test. La meta inmediata de
la ejecución de unidades de
prueba en un sentidoliteral, es
acumular los resultados de estas.
La clase TestResult sirve a este
propósito. Cada vez que una
prueba es ejecutada, el objeto
TestResult es pasado a una
colección de resultados.
Figura: La clase TestResult, usada
para coleccionar las salidas de las
pruebas:
55. xUnit::TestResult II
TestResult es un objeto simple,cuenta las
pruebas ejecutadas y almacena los pruebas
fallidas y los errores de forma que el entorno
puede devolver información en base a esto. Éstos
fallos y errores incluyen detalles acerca de la
localización en el código donde han ocurrido y
cualquier descripción asociada a la prueba. La
información impresa para el fallo de PruebaLibro
es un ejemplo.
56. Resumen de la arquitectura xUnit
Las clases TestCase , TestRunner,
TestFixture, TestSuite y TestResult
representan el núcleo de la
arquitectura xUnit.Comprender qué
hacen es comprender cómo funciona
xUnit.
58. Clases de prueba: PruebasLibreria,
PruebaLibreria y PruebaLibro
PruebasLibreria es una TestSuite que contiene una PruebaLibro y una
PruebaLibreria,ésta es una TestFixture y PruebaLibro es una TestCase.
Conceptualmente, TextTestRunner ejecuta PruebasLibreria, que al
ejecuta PruebaLibro y PruebaLibreria,las cuales ejecutan sus métodos
de forma alternada.