1 junit y eclemma

12
Politécnico Grancolombiano 10 Herramientas de Desarrollo JUnit y EclEmma Diego A. Cabra

Upload: carlos-londono

Post on 29-Dec-2015

118 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 1 JUnit y EclEmma

Politécnico Grancolombiano

10

Herramientas de

Desarrollo JUnit y EclEmma

Diego A. Cabra

Page 2: 1 JUnit y EclEmma

Herramientas de desarrollo

JUNIT Y ECLEMMA

La prueba puede ser definida como:

«Una actividad en la cual un sistema o componente es ejecutado bajo

condiciones específicas, se observan o almacenan los resultados y se realiza

una evaluación de algún aspecto del sistema o componente.» [IEE E90]

Durante su desarrollo, el software atraviesa diferentes etapas y estados que hacen

parte de un proceso evolutivo que comúnmente se conoce como ciclo de vida del

software. Aunque para algunos desarrolladores, la primera y última etapa consiste

en la implementación del sistema, el trabajo de un desarrollador no termina en el

momento que entrega el producto. Es claro que el producto es susceptible a fallos

y que no es muy probable que se detecten durante el proceso de implementación,

por tanto la posibilidad de que el desarrollador deba retomar su trabajo luego de la

entrega final es bastante alta. Lo delicado aquí es en qué momento surgirán estos

fallos, y cuál será su costo de reparación. No sería un buen momento para

percatarse de que el sistema de trazado de rutas de vuelos comerciales tiene un

peligroso desfase cuando hay miles de aviones recorriendo las rutas que ese

sistema diseñó. Es por esto que dentro del ciclo de vida del software debe

contemplarse una etapa dedicada a probar la aplicación en busca de estos fallos

para poderlos corregir con el menor costo posible. La etapa de pruebas es

fundamental para asegurar la calidad y confiabilidad del sistema.

El testing es el proceso de encontrar diferencias entre el comportamiento

esperado (requerido) y el comportamiento observado (existente) en los sistemas

implementados. El testing intenta mostrar que la implementación del sistema es

inconsistente con el modelo de sistema especificado. La aplicación de pruebas

tiene como objetivo identificar posibles fallos, errores, o defectos en la

implementación, diseño, calidad y/o usabilidad de un proyecto de software. La

diferenciación entre fallos, errores y defectos no es fortuita y cabe distinguir a que

se refiere cada uno de estos conceptos

Page 3: 1 JUnit y EclEmma

Los fallos son cualquier diferencia entre el comportamiento observado y el comportamiento esperado.

Un error representa que el sistema está en un estado que más adelante provocará un fallo del sistema, lo que implica que el sistema se desvía del comportamiento esperado.

Un defecto es la causa mecánica o algorítmica de un estado erróneo. Con las pruebas se busca verificar, analizar y evaluar la calidad de un producto de

software. Las pruebas se elaboran orientadas a quebrantar el sistema objetivo y a

maximizar la cantidad de defectos identificados, no buscan comprobar que el

sistema no tiene fallas, sino permitir a los desarrolladores corregir los defectos a

tiempo, para incrementar la confiabilidad del sistema. Una prueba exitosa es

aquella que encuentra faltas o defectos.

La confiabilidad es una medida de éxito que asegura que el comportamiento

observado de un sistema se ajusta a la especificación de su comportamiento.

Encontrar a tiempo posibles fallas o errores es crucial, pues permite minimizar el

costo en tiempo y dinero de la reparación de tales fallas y permite cumplir con los

objetivos del proyecto. Se estima que aproximadamente la mitad del esfuerzo en

el desarrollo de un programa (tiempo y costos) se invierte en la fase de pruebas.

Por tal motivo, las pruebas deben planificarse con suficiente anticipación aplicando

diversas metodologías y estrategias.

Idealmente, se procuraría exponer el programa o el software a todas las

situaciones posibles con el fin de encontrar todos los potenciales fallos que este

pueda tener, pero tal intención es prácticamente imposible de llevar a cabo desde

el punto de vista humano, económico y de tiempo. Esto invoca que la perfección

en un programa quizás no pueda ser alcanzada ni garantizada, por lo que el

proceso de pruebas se convierte en un proceso indispensable, de sumo cuidado y

responsabilidad en procura de maximizar la cantidad de defectos encontrados que

finalmente conlleven a fallos, esto por medio de la aplicación de estándares y

metodologías que permitan que el consumo de recursos destinados a esta

actividad sean aceptables.

No es recomendable que las pruebas sean realizadas por el mismo equipo de

desarrollo. Para ser más eficaces las pruebas deben ser realizadas por un equipo

independiente al equipo de desarrollo, que puede concebir una visión más objetiva

del software.

El proceso de pruebas comienza en la primera fase de desarrollo, con la

elaboración de un plan de pruebas donde se asignan los recursos y los horarios

Page 4: 1 JUnit y EclEmma

de las pruebas; a partir de este plan, durante la fase de implementación, se detalla

el diseño de las pruebas como tal, se prepara y se configura el software sobre el

que se van a ejecutar los casos de prueba y seguidamente, ya en la etapa de

pruebas, se ejecutan las pruebas especificadas. Al finalizar el proceso, se hace

una comparación entre la salida esperada y los resultados generados por las

pruebas, lo que resulta en información importante para la toma de medidas

adecuadas con el fin de minimizar o corregir los fallos.

Un caso de prueba es un conjunto de entradas y resultados esperados que se

aplica a un componente con el propósito de causar fallos y detectar defectos.

Un componente es una parte del sistema que puede ser aislada para hacer

pruebas (objeto, grupo de objetos, uno o más subsistemas).

Diseñar un caso de prueba consiste en elegir aquellas pruebas que por sus

características particulares se diferencian del resto. De esta forma se asume, que

si al ejecutar estos casos de prueba no se presentan errores o fallos se puede

tener una cierta garantía de que el programa no tiene defectos.

Las pruebas deben empezar por lo pequeño y progresar a lo grande, es por esto

que existen diferentes niveles de pruebas:

Pruebas Unitarias

Pruebas de integración

Pruebas del sistema

Pruebas funcionales

Pruebas de desempeño

Pruebas piloto

Pruebas de aceptación

Pruebas de instalación

Pruebas Unitarias: Son pruebas que se aplican sobre un único componente. Las

pruebas unitarias sirven para asegurarse que cada unidad de código o

componente funciona correctamente por separado, minimizando de esta forma la

complejidad de las pruebas generales. Las pruebas unitarias verifican la estructura

de cada componente y permite el paralelismo en las actividades de pruebas ya

que cada componente puede ser testeado o probado independientemente de los

otros.

Pruebas de integración: Estas pruebas detectan los defectos que no fueron

identificados durante las pruebas unitarias. Estas pruebas consisten en probar o

Page 5: 1 JUnit y EclEmma

testear dos o más componentes integrados. Si el resultado de estas pruebas no

revela defectos se integran más componentes al grupo. Estas pruebas consisten

en verificar que un gran conjunto de porciones de código funcionan correctamente

juntos.

Pruebas del sistema: Una vez los componentes han sido integrados y probados,

las pruebas de sistema aseguran que el sistema completo cumple con los

requerimientos funcionales y no funcionales. Las pruebas del sistema, prueban

todos los componentes juntos, vistos como un solo sistema para identificar

defectos. Busca encontrar diferencias entre el programa desarrollado y sus

requerimientos funcionales y no funcionales.

Dentro de las pruebas del sistema se realizan cinco tipos de pruebas diferentes,

estas son:

Pruebas funcionales: Las pruebas funcionales buscan encontrar las

diferencias entre los requerimientos funcionales y el desempeño del sistema. Para

esta actividad los casos de prueba definidos son derivados de los casos de uso. El

objetivo es seleccionar aquellas pruebas que son representativas para el usuario y

que tienen alta probabilidad de evidenciar un fallo.

Pruebas de desempeño: Las pruebas de desempeño buscan encontrar las

diferencias entre los requerimientos no funcionales especificados en el diseño y el

desempeño del sistema. En esta actividad se realizan pruebas de seguridad, de

sincronización, de recuperación, de capacidad de almacenamiento y de respuesta

a solicitudes.

Pruebas piloto: Las pruebas piloto consisten en instalar el sistema en

equipos que simulan las características del entorno real en donde será usado

finalmente el software, una vez instalado, el software es usado por un conjunto de

usuarios seleccionados; lo que permite que estos usuarios identifiquen fallos o

defectos antes de liberar el software.

Pruebas de aceptación: El objetivo de estas pruebas es validar que el

sistema cumple con el funcionamiento deseado o esperado. Estas pruebas son

definidas por el cliente o el usuario del sistema y representan las condiciones

típicas bajo las cuales el sistema debe operar. Estas pruebas le permiten al

usuario o cliente aceptar el sistema desde el punto de vista funcional y de

rendimiento.

Pruebas de instalación: Se comprueba el correcto funcionamiento del

sistema y el cumplimiento de los requerimientos no funcionales especificados,

instalado en su entorno real. Se revisan que los manuales de instalación

correspondan a la correcta instalación del sistema. Generalmente, durante las

Page 6: 1 JUnit y EclEmma

pruebas de instalación se repiten los casos de prueba ejecutados durante las

pruebas funcionales y de desempeño pero en el ambiente real.

Para llevar a cabo el proceso de pruebas, es necesario contar con profesionales

altamente calificados y capacitados. Estos profesionales deben saber elegir los

casos de prueba adecuados, deben conocer el lenguaje de desarrollo y el manejo

de herramientas especializadas, además de conocer y aplicar efectiva y

eficientemente metodologías y técnicas de pruebas. El equipo de pruebas del

sistema debe contar con un detallado entendimiento de todo el sistema, desde los

requerimientos, el diseño y la implementación.

Actualmente existen diversas herramientas de pruebas que efectúan y apoyan el

proceso de pruebas de software. Encontramos herramientas usadas para el

seguimiento de defectos, herramientas de carga y rendimiento, herramientas de

gestión y manejo de pruebas, herramientas para pruebas de unidad y de

cubrimiento de código, y herramientas especializadas. En esta unidad

estudiaremos específicamente dos herramientas, JUnit que lleva a cabo el

desarrollo de pruebas unitarias y Emma que lleva a cabo pruebas de cobertura de

código.

JUNIT: HERRAMIENTA PARA PRUEBAS UNITARIAS

Como se mencionó anteriormente, el proceso de pruebas puede llegar a ser un

proceso costoso en tiempo y recursos, por lo que es recomendable diseñar y

ejecutar pruebas unitarias que minimicen la complejidad de las pruebas generales.

Una buena práctica para llevar a cabo estas pruebas unitarias es que luego de

implementar un método, clase o componente (según las necesidades y la

complejidad del proyecto), se lleve a cabo inmediatamente el caso o los casos de

prueba necesarios para comprobar y asegurar que el comportamiento, de lo que

se ha acabado de implementar, coincida con el comportamiento esperado. Por

ejemplo, se elaboró un método que suma dos enteros y devuelve la suma de los

enteros al cuadrado. En lugar de comprobar que el método funciona

correctamente por medio de mensajes en la consola (System.out.println()) que no

es una buena práctica, se desarrolla un caso de prueba que permite comprobar el

funcionamiento del método sin afectar o alterar el código del mismo.

Entre las herramientas para pruebas unitarias encontramos una muy potente

llamada JUnit, en la cual concentraremos nuestra atención, pues esta herramienta

automatiza la ejecución de este tipo de pruebas en programas escritos en Java.

Page 7: 1 JUnit y EclEmma

JUnit es una herramienta de pruebas gratuita creada por Kent Beck y Erich

Gamma quienes suman una amplia experiencia en el trabajo con patrones de

diseño y la metodología de desarrollo de software extreme programming (XP).

JUnit es un conjunto de bibliotecas que permiten ejecutar de forma controlada una

clase java, con el fin de evaluar el funcionamiento sus métodos. Esta herramienta

es muy popular entre los desarrolladores de software, debido a su facilidad de uso.

Con JUnit se pueden desarrollar casos de prueba (Test Case) y colecciones de

pruebas (Suite Test). JUnit ofrece una interfaz gráfica que permite visualizar y

ejecutar las pruebas, verificar los resultados, entre otras acciones.

En JUnit los casos de prueba son clases que disponen de métodos para probar

los métodos de una clase o un modulo completo. Para cada clase que se quiera

probar por medio de JUnit es necesario definir una clase de caso de prueba e

implementar los métodos de prueba deseados. Una Suite test es una clase java

que ejecuta uno o varios casos de prueba.

La última versión de JUnit (v.4) simplifica aun más el trabajo, en comparación con

la versión anterior (v.3), ya que aprovecha las anotaciones, una característica de

Java disponible desde la versión 1.5 del JDK. Las anotaciones son observaciones

que se pueden añadir a una clase, un método, un atributo etc. para describirlos

mejor. Una anotación son una especie de marcas o etiquetas, que agregan

metadatos al código fuente; por medio de estos metadatos se le indica a la JVM

como interactuar con los elementos del programa.

En el caso de JUnit v.4, se eliminaron las convenciones de nombrado para ubicar

los métodos de prueba. En cambio ahora, para señalar que un método es un

método de prueba se coloca antes de la declaración del método la anotación

@Test, esto también elimina la necesidad de heredar la clase de prueba de la

clase TestCase y de colocar el prefijo test a cada uno de los métodos de prueba,

que en las versiones anteriores era totalmente necesario.

JUnit cuenta con varios métodos que permiten hacer las comprobaciones

necesarias a los métodos de la clase que está siendo probada. Estos métodos

comparan el comportamiento observado u obtenido al ejecutar el caso, con el

comportamiento esperado.

Page 8: 1 JUnit y EclEmma

Ejemplos de estos métodos son:

Métodos assertXXX() Que comprueba

assertTrue(expresión) Este método comprueba que al evaluarse la expresión esta devuelva el valor booleano true. Devuelve assertionError si no se produce el resultado esperado

assertFalse(expresión) Este método comprueba que al evaluarse la expresión esta devuelva el valor booleano false.

assertEquals(comportamiento esperado, comportamiento actual)

Comprueba que el comportamiento esperado sea igual al comportamiento actual o real. Devuelve assertionError si no se produce el resultado esperado.

assertNull(Objeto) Comprueba que la referencia al Objeto sea nula (“null”).

assertNotNull(Objeto) Comprueba que la referencia al Objeto no sea nula (“null”).

assertSame(Objeto esperado, Objeto real)

Comprueba que el objeto esperado y el objeto real sean el mismo objeto.

assertNotSame(Objeto esperado, Objeto real)

Comprueba que el objeto esperado y el objeto real no sean el mismo objeto.

Fail() Devuelve una alerta informando el fallo del test.

assertArrayEquals Recibe como parámetro 2 arreglos y comprueba si son iguales. Devuelve assertionError si no se produce el resultado esperado.

La lista completa de todos los métodos que se pueden usar para hacer pruebas, la

pueden consultar en el siguiente link del API de JUnit:

http://junit.org/apidocs/org/junit/Assert.html

Además de la anotación @Test, existen otras anotaciones que se colocan antes

de los métodos, las principales son:

@BeforeClass: El método que tenga antes de su implementación esta anotación

será invocado al principio, es decir antes del lanzamiento de todas las pruebas.

Este método suele ser utilizado para inicializar atributos comunes para todas las

pruebas o también para desempeñar una actividad que generalmente consume

mucho tiempo, por ejemplo la conexión a una base de datos. En una clase de

Page 9: 1 JUnit y EclEmma

prueba solo puede existir un único método con esta anotación y solo puede ser

invocado una sola vez.

@AfterClass: Este método será invocado una sola vez, cuando finaliza la

ejecución de todas las pruebas. Al igual que el anterior, solo puede haber un

método asociado con esta anotación. Este método suele ser utilizado para liberar

recursos, por ejemplo la desconexión a una base de datos.

@Before: En un método con esta anotación se permite reunir instrucciones de

inicialización de objetos comunes para todos los métodos prueba. Comúnmente,

se usa para configurar o preparar el ambiente de prueba para los métodos. Esto

es importante, ya que suele suceder que los métodos de prueba usan un mismo

objeto en común modificando sus atributos o estado, para efectuar las respectivas

comprobaciones; si el siguiente método de prueba toma este mismo objeto con

tales modificaciones, puede provocar que los resultados que arroja su ejecución

no sean los esperados.

@After: Un método con esta anotación reúne las instrucciones necesarias liberar

los recursos de cada test, por ejemplo destruir referencias de objetos. Los

métodos con esta anotación serán invocados después de terminar cada prueba.

@Ignore: Los métodos que son marcados con esta anotación no serán

ejecutados. Esta anotación suele colocarse cuando un método de prueba no

puede o no debe ser ejecutado por algún motivo.

De manera más concreta, la anotación @Test puede tener dos tipos de

modificadores:

@Test (timeout = X): significa que la prueba será válida si se ejecuta en un tiempo

máximo de X milisegundos.

@Test (expected = java.util.NoSuchElementException.class): significa que la

prueba será válida solo si se lanza la excepción esperada.

Pruebas de cobertura

Antes de finalizar, es importante mencionar que existen también otras

herramientas desarrolladas bajo la tecnología java que pueden ser integradas a

Eclipse y que pueden proveer información importante acerca de la ejecución de

pruebas.

Las pruebas unitarias como las que se trabajan con JUnit se denominan pruebas

de caja negra, pues al ejecutar la prueba, la implementación del modulo no tiene

Page 10: 1 JUnit y EclEmma

importancia, es decir no importa como hace las cosas, el caso es que las haga

bien; ahora si las hace mal, pues habrá que hacer cambios en la implementación,

pero esto está fuera del alcance de la prueba. Otro estilo de pruebas son las

denominadas de caja blanca o caja de cristal, las cuales se centran en los detalles

procedimentales de los módulos, no solo importa lo que hace el modulo, sino

también como hace las cosas. Estas pruebas están muy ligadas al código fuente,

ya que para un caso de prueba no solo se analiza el resultado, sino el flujo de

ejecución del programa. Se pueden aplicar en diferentes niveles (unidad,

integración y sistema) pero es habitual que se apliquen únicamente como pruebas

unitarias.

Un tipo de prueba de caja blanca aplicable al nivel de unidad, son las pruebas de

cobertura, las cuales indican que porcentaje del código fuente de un modulo se ha

ejecutado al aplicar un caso de prueba. Esta medida se puede usar para detallar

que tan efectiva es una prueba, ya que si con los casos ejecutados, por ejemplo,

solo se ha cubierto un 50% del código, indica que la prueba se hizo a medias

porque hay otro 50% de código que no se ha probado y por tanto es susceptible a

fallos.

ECLEMMA: HERRAMIENTA PARA PRUEBAS DE COBERTURA

EclEmma es una herramienta de cobertura de código para Eclipse, basada en

Emma. Emma es un kit de herramientas de código abierto, que fue diseñado

exclusivamente para determinar el porcentaje de cobertura de código cuando se

realiza una prueba o cuando simplemente se ejecuta el código. La cobertura de

código es una medida que determina el porcentaje de código que se ha ejecutado

bajo condiciones específicas. Con las pruebas de caja blanca lo que buscamos es

encontrar fragmentos del código del programa que no son ejecutados por los

casos de pruebas. El objetivo principal es procurar que la ejecución de las pruebas

de caja blanca cubra el 100% del código.

Si encontramos que el resultado de las pruebas es menor al 100%, debemos

ejecutar otros casos de prueba para intentar llegar a un umbral muy cercano o

igual al 100%. Si aun así no conseguimos cubrir la totalidad de las sentencias

ejecutadas, se debe verificar si aquel pequeño porcentaje de código que no se ha

cubierto es totalmente necesario, pues quizás se podría prescindir de él ya que

podría ser un segmento inalcanzable por el algoritmo.

Por lo general, las pruebas terminan antes de alcanzar el 100%, pues procurar

cubrir el total de las sentencias puede ser un trabajo excesivamente arduo y

costoso.

Page 11: 1 JUnit y EclEmma

El nivel de cobertura aceptable para una prueba depende de lo crítico que sea el

código que está siendo probado. En algunos casos una cobertura del 60-80% de

código puede ser aceptable. Pero en otros casos, como en el software

desarrollado para llevar a cabo procesos en empresas donde la confiabilidad de

los datos y la integridad de ellos sean de vital importancia, la cobertura de código

debe procurarse a un 100%. Un ejemplo muy común, es el software que se

desarrolla para el manejo de cuentas de un banco. Obligatoriamente, antes de

liberarse un programa de esta índole, se debe garantizar que la cantidad de

código que cubrió el desarrollo de las pruebas este muy cerca del umbral del

100%, pues no se puede descartar que en la mínima porción de código que no se

probó, se pueda presentar un error que implique grandes pérdidas para el banco o

para los clientes.

La cobertura requerida suele incrementar cuando se desarrollan aplicaciones

donde el control de los datos es muy delicado, por ejemplo: aplicaciones

hospitalarias, aplicaciones para centrales nucleares o aplicaciones que necesitan

cálculos precisos como aplicaciones bancarias y aplicaciones militares; también de

acuerdo al ámbito previsto de distribución del software desarrollado, ya que si un

software se distribuye a nivel mundial, de presentarse un error sería muy costoso

redistribuirlo de nuevo. Incluso a estos niveles suele ser necesario replantear el

sistema de pruebas de la aplicación y tal vez optar por métodos de verificación

formal de los algoritmos.

JUNIT, ECLEMMA Y ECLIPSE IDE

En la última versión de Eclipse (Eclipse 3.5.2 Galileo, al fecha de este documento),

viene integrada de facto la última versión de JUnit (JUnit v. 4), aunque también

viene integrada por defecto la versión anterior. Se dice que la herramienta JUnit

está integrada al IDE porque no es necesario descargar e instalar ningún plug-in

para tener esta funcionalidad (para anteriores versiones de Eclipse era necesario).

Tal como vimos en la primera unidad de este modulo, todas las herramientas del

JDK están por defecto integradas en el IDE, sin embargo JUnit no es una

herramienta que hace parte del JDK, ya que JUnit es el resultado del trabajo de

un equipo de desarrollo, quienes hicieron de esta herramienta una de las más

populares para construir y aplicar pruebas unitarias, lo que ocasionó que

progresivamente cada vez mas desarrolladores la usaran en sus proyectos. Por tal

motivo las últimas versiones de eclipse traen integrada esta herramienta, con el

objetivo de ofrecer a los desarrolladores un entorno de desarrollo que cumpla con

sus requerimientos y expectativas. Sin embargo, JUnit también puede ser usada

Page 12: 1 JUnit y EclEmma

por medio de líneas de comandos, así como son usadas algunas herramientas del

JDK, tal como lo aprendimos en la primera unidad de este modulo.

Aunque las diferencias entre las versión 3 y 4 de JUnit son importantes, en el

desarrollo de esta unidad solo tendremos en cuenta para los ejemplos y las

actividades practicas la versión 4, por tal motivo no entraremos en detalles del

funcionamiento de la versión 3.

El manejo de JUnit y EclEmma desde Eclipse es muy sencillo e intuitivo. Cuando

se decide realizar un caso de prueba para una clase determinada, el asistente de

configuración proporciona diferentes opciones para la creación del caso de

prueba. Sin embargo, es importante aclarar que aunque Eclipse permita crear

estas clases de casos de prueba de manera automática, no implementa

funcionalidad a ninguno de los métodos prueba que se desean ejecutar, pues

esta, es directamente la función del encargado de elaborar o implementar las

pruebas.

A diferencia de JUnit, EclEmma es un plug-in que debe ser descargado e

integrado al entrono de desarrollo para acceder a su funcionalidad.

De acuerdo a lo anterior, la práctica y desarrollo de los contenidos de esta unidad,

les permitirá comprender mejor la funcionalidad y el manejo de JUnit y EclEmma

desde el IDE Eclipse.