miércoles, 1 de septiembre de 2010

DESARROLLO DIRIGIDO POR PRUEBAS

BLOG DETALLADO DEL CURSO.


El desarrollo guiado por pruebas, o Test-driven development (TDD) es una práctica de programación que involucra otras dos prácticas: Escribir las pruebas primero (Test First Development) y Refactorización (Refactoring).

Para escribir las pruebas generalmente se utilizan las PruebasUnitarias (unit test en inglés).
  • En Primer Lugar se escribe una prueba y se verifica que las pruebas fallen,
  • luego se implementa el código que haga que la prueba pase satisfactoriamente
  • y seguidamente se refactoriza el código escrito.

El propósito del desarrollo guiado por pruebas es lograr un código limpio que funcione

La idea es que los requerimientos sean traducidos a pruebas, de este modo, cuando las pruebas pasen se garantizará que los requerimientos se hayan implementado correctamente.



Contenido

* 1 Requisitos
* 2 Ciclo De Desarrollo Conducido por Pruebas
* 3 Características
* 4 Ventajas
* 5 Limitaciones
* 6 Véase también
* 7 Enlaces externos

Requisitos


Para que funcione el desarrollo guiado por pruebas, el sistema que se programa tiene que ser lo suficientemente flexible como para permitir que sea probado automáticamente.

Cada prueba será suficientemente pequeña como para que permita determinar unívocamente si el código probado pasa o no la prueba que esta le impone.

El diseño se ve favorecido ya que se evita el indeseado "sobre diseño" de las aplicaciones y se logran interfaces más claras y un código más cohesivo.

Frameworks como JUnit o TestNG proveen de un mecanismo para manejar y ejecutar conjuntos de pruebas automatizadas.


Ciclo De Desarrollo Conducido por Pruebas


Antes de comenzar el ciclo se debe definir una lista de requerimientos. Luego se comienza el siguiente ciclo:

  1. Elegir un requerimiento: Se elige de una lista el requerimiento que se cree que nos dará mayor conocimiento del problema y que a la vez sea fácilmente implementable.
  2. Escribir una prueba: Se comienza escribiendo una prueba para el requerimiento. Para ello el programador debe entender claramente las especificaciones y los requisitos de la funcionalidad que está por implementar. Este paso fuerza al programador a tomar la perspectiva de un cliente considerando el código a través de sus interfaces.
  3. Verificar que la prueba falla: Si la prueba no falla es porque el requerimiento ya estaba implementado o porque la prueba es errónea.
  4. Escribir la implementación: Escribir el código más sencillo que haga que la prueba funcione. Se usa la metáfora "Déjelo simple" ("Keep It Simple, Stupid" (KISS)).
  5. Ejecutar las pruebas automatizadas: Verificar si todo el conjunto de pruebas funciona correctamente.
  6. Eliminación de duplicación: El paso final es la refactorización, que se utilizará principalmente para eliminar código duplicado. Se hacen de a una vez un pequeño cambio y luego se corren las pruebas hasta que funcionen.
  7. Actualización de la lista de requerimientos: Se actualiza la lista de requerimientos tachando el requerimiento implementado. Asimismo se agregan requerimientos que se hayan visto como necesarios durante este ciclo y se agregan requerimientos de diseño (P. ej que una funcionalidad esté desacoplada de otra).


Tener un único repositorio universal de pruebas facilita complementar TDD con otra práctica recomendada por los procesos ágiles de desarrollo, la "Integración Frecuente". Integrar frecuentemente nuestro trabajo con el del resto del equipo de desarrollo permite ejecutar toda batería de pruebas y así descubrir si nuestra última versión es compatible con el resto del sistema. Es recomendable y menos costoso corregir pequeños problemas cada pocas horas que enfrentarse a problemas enormes cerca de la fecha de entrega fijada.


Características


  1. Una ventaja de esta forma de programación es el evitar escribir código innecesario ("You Ain't Gonna Need It" (YAGNI)). Se intenta escribir el mínimo código posible, y si el código pasa una prueba aunque sepamos que es incorrecto nos da una idea de que tenemos que modificar nuestra lista de requerimientos agregando uno nuevo.
  2. La generación de pruebas para cada funcionalidad hace que el programador confíe en el código escrito. Esto permite hacer modificaciónes profundas del código (posiblemente en una etapa de mantenimiento del programa) pues sabemos que si luego logramos hacer pasar todas las pruebas tendremos un código que funcione correctamente.
  3. Otra característica del Test Driven Development requiere que el programador primero haga fallar los casos de prueba. La idea es asegurarse de que los casos de prueba realmente funcionen y puedan recoger un error.


Ventajas


Los programadores que utilizan el desarrollo guiado por pruebas en un proyecto encuentran que en raras ocasiones tienen la necesidad de utilizar el depurador o debugger.

A pesar de los elevados requisitos iniciales de aplicar esta metodología, el desarrollo guiado por pruebas (TDD) puede proporcionar un gran valor añadido en la creación de software, produciendo aplicaciones de más calidad y en menos tiempo. Ofrece más que una simple validación del cumplimiento de los requisitos, también puede guiar el diseño de un programa. Centrándose en primer lugar en los casos de prueba uno debe imaginarse cómo los clientes utilizarán la funcionalidad (en este caso, los casos de prueba). Por lo tanto, al programador solo le importa la interfaz y no la implementación. Esta ventaja es similar al diseño por convenio pero se parece a él por los casos de prueba más que por las aserciones matemáticas.

La potencia del TDD radica en la capacidad de avanzar en pequeños pasos cuando se necesita. Permite que un programador se centre en la tarea actual y la primera meta es a menudo hacer que la prueba pase. Inicialmente no se consideran los casos excepcionales y el manejo de errores. Estos, se implementan después de que se haya alcanzado la funcionalidad principal.

Otra ventaja es que, cuando es utilizada correctamente, se asegura de que todo el código escrito está cubierto por una prueba. Esto puede dar al programador un mayor nivel de confianza en el código.

Limitaciones


El desarrollo guiado por pruebas requiere que las pruebas puedan automatizarse. Esto resulta complejo en los siguientes dominios:

  • Interfaces Gráfica de usuario (GUIs), aunque hay soluciones parciales propuestas. (Ej: Selenium)
  • Objetos distribuidos, aunque los objetos simulados (MockObjects) pueden ayudar. (Ej: EasyMock o Mockito)
  • Bases de datos. (Ej: DBUnit) Hacer pruebas de código que trabaja con base de datos es complejo porque requiere poner en la base de datos unos datos conocidos antes de hacer las pruebas y verificar que el contenido de la base de datos es el esperado después de la prueba. Todo esto hace que la prueba sea costosa de codificar, aparte de tener disponible una base de datos que se pueda modificar libremente.

PruebasDeCajaNegra
Las pruebas de caja negra, no tienen en cuenta el funcionamiento interno del programa, sino unicamente el resultado de las comunicaciones entre objetos


PruebasUnitarias

En programación, una prueba unitaria es una forma de probar el correcto funcionamiento de un módulo de código. Esto sirve para asegurar que cada uno de los módulos funcione correctamente por separado. Luego, con las PruebasDeIntegración, se podrá asegurar el correcto funcionamiento del sistema o subsistema en cuestión.

La idea es escribir casos de prueba para cada función no trivial o método en el módulo de forma que cada caso sea independiente del resto.


Características


Para que una prueba unitaria sea buena se deben cumplir los siguientes requisitos:

  • Automatizable: no debería requerirse una intervención manual. Esto es especialmente útil para integración continua.
  • Completas: deben cubrir la mayor cantidad de código.
  • Repetibles o Reutilizables: no se deben crear pruebas que sólo puedan ser ejecutadas una sola vez. También es útil para integración continua.
  • Independientes: la ejecución de una prueba no debe afectar a la ejecución de otra.
  • Profesionales: las pruebas deben ser consideradas igual que el código, con la misma profesionalidad, documentación, etc.

Aunque estos requisitos no tienen que ser cumplidos al pie de la letra, se recomienda seguirlos o de lo contrario las pruebas pierden parte de su función.

Ventajas


El objetivo de las pruebas unitarias es aislar cada parte del programa y mostrar que las partes individuales son correctas. Proporcionan un contrato escrito que el trozo de código debe satisfacer. Estas pruebas aisladas proporcionan cinco ventajas básicas:

  1. Fomentan el cambio: Las pruebas unitarias facilitan que el programador cambie el código para mejorar su estructura (lo que se ha dado en llamar refactorización), puesto que permiten hacer pruebas sobre los cambios y así asegurarse de que los nuevos cambios no han introducido errores.
  2. Simplifica la integración: Puesto que permiten llegar a la fase de integración con un grado alto de seguridad de que el código está funcionando correctamente. De esta manera se facilitan las pruebas de integración.
  3. Documenta el código: Las propias pruebas son documentación del código puesto que ahí se puede ver cómo utilizarlo.
  4. Separación de la interfaz y la implementación: Dado que la única interacción entre los casos de prueba y las unidades bajo prueba son las interfaces de estas últimas, se puede cambiar cualquiera de los dos sin afectar al otro, a veces usando objetos mock (mock object) para simular el comportamiento de objetos complejos.
  5. Los errores están más acotados y son más fáciles de localizar: dado que tenemos pruebas unitarias que pueden desenmascararlos.

Limitaciones


Es importante darse cuenta de que las pruebas unitarias no descubrirán todos los errores del código. Por definición, sólo prueban las unidades por sí solas. Por lo tanto, no descubrirán errores de integración, problemas de rendimiento y otros problemas que afectan a todo el sistema en su conjunto. Además, puede no ser trivial anticipar todos los casos especiales de entradas que puede recibir en realidad la unidad de programa bajo estudio. Las pruebas unitarias sólo son efectivas si se usan en conjunto con otras pruebas de software.

Algunas de las herramientas disponibles


  • JUnit: Entorno de pruebas para Java creado por Erich Gamma y Kent Beck. Se encuentra basado en SUnit creado originalmente para realizar pruebas unitarias para el lenguaje Smalltalk.
  • TestNG: Creado para suplir algunas deficiencias en JUnit.
  • JTiger: Basado en anotaciones, como TestNG.
  • SimpleTest: Entorno de pruebas para aplicaciones realizadas en PHP.
  • PHPUnit: framework para realizar pruebas unitarias en PHP.
  • CPPUnit: Versión del framework para lenguajes C/C++.
  • NUnit: Versión del framework para la plataforma.NET.
  • MOQ : Framework para la creacion dinamica de objetos simuladores (mocks). http://code.google.com/p/moq/

--> framework TestNG:


Para hacer pruebas se sigue la siguiente ruta de directorios:

Clase funcional: es.cea.servicios.ServicioRegistro --> en carpeta "src".
Clase de prueba: es.cea.servicios.ServicioRegistroTest --> en carpeta "Test"


INSTALAR Y EJECUTAR TestNG:

  1. descargamos e instalamos el plugin para eclipse y de esa forma poder lanzar las pruebas desde eclipse a través del comando RUN AS: TESTNG TEST. http://testng.org/doc/download.html
  2. descargamos e instalamos la libreria en nuestro proyecto a desarrollar para poder utilizar las clases incluidas en la librería http://testng.org/doc/download.html

Escribir mi primera prueba

--Utilizamos la etiqueta @Test para indicar los metodos de prueba: http://code.google.com/p/cea2010-jar/source/browse/trunk/clase01/test/es/cea/aeropuerto/TerminalAeropuertoTest.java?spec=svn12&r=12

COMO HACER LA PRUEBA:

@Test
public void prueba VentaBilletesTerminal(){

Obj Probar --> TerminalAeropuerto ta=new TerminalAeropuerto();

Escenario Prueba --> String destino="Cadiz";
int nBilletes=3;

Invocacion --> Resultado r=ta.ventaBillete(destino,nBilletes);

Comprobacion Mensaje --> assert(r==x) : "quillo esto no funciona"; //Lo de dentro de assert
//debe ser verdadero
}
  1. Utilizamos la etiqueta @DataProvider para probar con diferentes valores. Enlace a documentación: http://testng.org/doc/documentation-main.html#parameters-dataproviders .Enlace a código: http://code.google.com/p/cea2010-jar/source/browse/trunk/clase01/test/es/cea/aeropuerto/TerminalAeropuertoTest.java?spec=svn13&r=13

PROBAR CON DIFERENTES VALORES:

@Test
public void (nombre prueba) (Object o, Object o2.....){

Object resultadoReal=ta.(nombre prueba)();

assert (resultadoReal==resultadoEsperado) : "______"; //Esperamos TRUE
}

En el enlace de juan antonio del codigo se puede ver mas claro el codigo de @DataProvider
del aeropuerto

Etiquetas Interesantes

@BeforeTest: se invoca una vez para todos los metodos de prueba.
@BeforeMethod: se invoca una vez para cada metodo de prueba.
@AfterTest: finalizacion de recurso. (@BeforeTest, @AfterTest --> se utilizan para Base de Datos).
@AfterMethod: cuando acaba cada prueba, si existe un metodo con esta inicializacion se ejecuta


CAMBIAR VERSION DE PROYECTO (SUBVERSION)

Proyecto (boton derecho) --> Team --> Switch --> Revision


Para el proyecto "picacea" --> Repository juan antonio, primero para ver como va la cosa, abrimos la version 33, con el metodo antes explicado.

No hay comentarios:

Publicar un comentario