lunes, 2 de agosto de 2010

TDD: Test Driven Development

Martin Fowler escribe en el Capítulo 4 de su libro "Refactoring":


"Si uno mira en qué gastan su tiempo muchos programadores, encontrará que escribir código ocupa una pequeña fracción. Parte del tiempo se emplea tratando de ver cómo seguir, otra parte en diseño, pero la mayor parte del tiempo se gasta en depuración. Estoy seguro que cada lector de este libro puede recordar largas horas de depuración, muchas veces por la noche. Cada programador puede contar la historia de un "bug" que tomó todo un día (o más) en encontrarse. Arreglar el "bug" es, generalmente, algo rápido, pero encontrarlo es una pesadilla. Y cuando uno arregla un "bug", hay siempre la posibilidad de que otro aparecerá y que Ud. no se dará cuenta hasta mucho tiempo más tarde. Y entonces, tardará más tiempo encontrando ese nuevo "bug"."


TDD en Wikipedia
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 pruebas unitarias (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 (Del inglés: Clean code that works). 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.




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 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.

Caracteristicas

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.
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.
Otra cracterí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 virgen 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.
El poder 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.
  • Objetos distribuidos, aunque los objetos simulados (MockObjects) pueden ayudar.
  • Bases de datos. 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.




¿Cómo de grandes deben ser cada iteración?
Hemos visto en el ejemplo de la suma que hemos hecho un test para la suma, hemos hecho una implementación inmediata y luego hemos refactorizado para llegar a una implementación buena. Estos pasos son excesivos para una cosa tan simple. En un proyecto real, ¿cómo de grandes son los pasos?. La solución depende de nosotros, de nuestra experiencia y de nuestra capacidad para programar.
Los test que hagamos no deben ser muy triviales, de forma que no nos eternicemos haciendo test tontos que se resuelven en cuestión de segundos. Tampoco deben ser muy complejos, de forma que un test no puede llevarnos dos días para codificarlo y otra semana más para hacer el código necesario para que pase el test y otra semana para hacer el refactor.
Debemos hacer test hasta el punto de complejidad en el que todavía nos sintamos cómodos programando y teniendo las cosas en la cabeza, pero sin llegar a ser triviales, es decir, que no nos aburramos, pero que tampoco tengamos que pasar ratos largos pensando qué hacer para resolver el test. Por eso, el límite depende de nuestra capacidad y experiencia. Prácticamente todos somos capaces de implementar un método suma() a la primera. Sin embargo, un método factorial()recursivo puede resultar demasiado para un aprendiz, aunque sea trivial para un programador con buena cabeza y varios años de experiencia. Un programador novato deberá hacer el factorial con un test para el caso trivial de factorial de 1, resolverlo, otro test para otro caso no trivial y resolverlo. El programador avanzado puede hacer el factorial() en un solo paso.
Lo ideal es que en hacer un test y el código necesario para que pase dicho test más la refactorización no se tarde más de un cuarto de hora/media hora. Una indicación clara de que estamos haciendo pasos muy grandes es que empiecen a fallarnos test inesperadamente. Una cosa es ser consciente de que hemos hecho una modificación que hace que falle un test antiguo y que lo vamos a arreglar un poco después, y otra cosa es que cuando creemos que ya hemos acabado, un test nos falle sin esperarlo.
No dejarse llevar mientras resolvemos un test
Una vez hecho el test y viendo que falla, debemos hacer el código mínimo necesario para que eso funcione. Es normal, y cualquier programador con experiencia me dará la razón, que mientras estamos codificando nos demos cuenta de posibles fallos, mejoras o necesidades en otras partes del código relacionadas con lo que estamos haciendo y que vayamos corriendo a hacerlas. Pues bien, eso es justo lo que NO debemos hacer. Debemos centrarnos en hacer que el test que hemos escrito pase lo antes posible.
Un ejemplo sencillo de esto. Imagina que hacemos un método para convertir un String a mayúsculas. Java ya tiene ese método, pero vamos a hacerlo. Nuestro test dice que si pasamos "pedro" como parámetro, el método nos debe devolver "PEDRO". Nos ponemos a codificar e inmediatamente empezamos a pensar ... "¿y si me pasan un parámetro null?. Seguro que el código rompe. Tengo que poner un if para comprobar el parámetro ...". Pues bien, eso es lo que NO hay que hacer. Codificamos el método suponiendo que el parámetro no es null y luego, más adelante, hacemos un segundo test para cuando nos pasen un parámetro null. Obviamente, esto vuelve a ser demasiado sencillo, en un caso real quizás el arreglo que creemos necesitar no tiene una solución tan rápida.
Para sentirnos cómodos dejando esas mejoras/modificaciones adicionales sin hacer, lo mejor es en cuanto las veamos, apuntar en una lista la necesidad de hacer más adelante un test para implementar esa mejora. Una vez apuntado y con la seguridad de que no se nos olvidará, podemos seguir codificando nuestro test actual. Una vez que ese test pasa y hemos hecho el refactor, podemos elegir otro de los test pendientes de la lista. En el ejemplo anterior, mientras codificamos el caso del método en el que el parámetro es correcto, se nos ocurre "¿qué pasa si me pasan un null?". Pues bien, lo apuntamos en la lista "hacer un test cuando el parámetro es null" y seguimos codificando nuestro test actual (devolver "PEDRO" cuando nos pasan "pedro").
Dejar que TDD nos lleve al diseño
El ejemplo de la suma es muy sencillo y la refactorización que hicimos para evitar duplicidades es bastante cuestionable. Veamos ahora un ejemplo algo más complejo en el que la refactorización nos lleva a un diseño más simple.
Supón que en un primer test debemos poder fijar el sueldo a un Jefe. Hacemos un test simple en el que a una clase Jefe llamamos al método setSueldo() y luego llamando a getSueldo() comprobamos que es correcto. Este primer test es bastante simple y no nos extendemos más.
Supón ahora, en un segundo test, que hacemos lo mismo con un Currito: Fijarle el sueldo. Hacemos un test similar, esta vez para la clase Currito, con su método setSueldo() y getSueldo().
Y ahora llega el momento de refactorizar y evitar duplicidades. ¿Qué tenemos duplicado? Pues dos clases Jefe y Currito que son exactamente iguales, salvo el nombre. Cualquier diseño Orientado a Objetos nos dirá que esto es correcto, que es adecuado hacer una clase para cada tipo de empledado. Pero TDD nos indica que debemos simplificar y evitar duplicidades, así que creamos una clase Empleado con métodos setSueldo() y getSueldo() y borramos las dos clases anteriores Jefe y Currito, además de rehacer los test. Ya tenemos un diseño mucho más simple (hay una sola clase y no hay código duplicado) que cumple perfectamente con ambos test.
Supón ahora un tercer test, en el que se quiere preguntar e Empleado si tiene derecho a coche de empresa y debe ser cierto sólo si es Jefe. Hacemos el test y ... ¿Volvemos a crear Jefe y Currito?. No, de momento no es necesario. Lo más simple es que empleado en el constructor admita un enumerado cuyos valores sean Jefe o Currito y se lo guarde. Luego, el método tieneCocheEmpresa() devolverá true o false en función del valor de ese enumerado.
Entonces, ¿cuándo debemos hacer las clases Jefe y Currito, heredando de Empleado o algo similar?. Pues bien. sólo cuando un test nos lo requiera y la solución más simple para ese test sea dividar la clase Empleado en dos. La condición para comprobar el enumerado es una condición que tampoco debe repetirse en el código. Si en nuestro código de la clase Empleado se empieza a repetir varias veces cosas como
if (TipoEmpleado.JEFE == tipoEmpleado) {
   ...
el evitar ese if duplicado en varios sitios empieza a invitar a pensar en otra solución, algo de herencia o quizás polimorfismo.





A medida que fue implementándose en distintos proyectos, tecnologías y lenguajes, se fueron formando patrones. Algunos ya aparecieron en las bases de los sistemas xUnit, y ya vienen incorporados ahí. Otros son patrones que adoptamos en el código de test que escribimos. Sirva esta lección de lista inicial de patrones (respetamos el término por el que se conoce en inglés):

Test Method: En lenguajes orientados a objetos, colocamos cada test en un método de alguna clase. Los frameworks de tests usan distintas estrategias para enumerar esos métodos.

Assertion Method:

Testcase Class:

Test Double:

Stub:

Mock:

Test Suite Object:

Four-Phase Test: Muchos de nuestros tests siguen un patrón de ejecución en cuatro etapas: inicialización, ejercitar el SUT, verificar, finalización.

   
   Hemos escrito algunos tests, y podemos descubrir un patrón:

1- Al principio, armamos todo lo necesario para la ejecución de lo que queremos probar.

2- Ejercitamos el software bajo prueba.

3- Comprobamos que la prueba sea exitosa: revisamos los resultados, el estado del SUT, otras salidas, etc.

4- Cerramos y liberamos los recursos usados

En el caso de nuestra pila, podríamos poner:

1- Creamos la pila

2- Probamos la pila: p.ej., hacemos dos Push.

3- Hacemos dos Pop viendo que sean los elementos del paso 2, en sentido inverso.

4- Liberamos la pila (implícitamente, al ser recolectada en algún momento por el Garbage Collector, el Recolector de Basura).

Podemos usar este gráfico (usando la terminología en inglés, que es conveniente también manejar):

Cuatro fases

En esta figura, los cuatro pasos son: Setup (inicialización), Exercise (ejercitar el SUT, Software Under Test), Verify (verificar), Teardown (finalización ordenada).



Test Fixture:

Fixture es un término que refiere a todo lo que se necesita para que el SUT funcione en nuestro test. Hasta ahora, en nuestros ejemplos sencillos, no necesitamos nada más que el SUT. Pero en otros casos más complejos, el SUT podría necesitar de otros objetos.


he aqui un ejemplo de  la vida  real  o aqui  y varios mas  aqui

TDD no es sólo codificar. Generalmente, viene precedido de un diseño de lo que queremos obtener como software funcionando. En metodologías ágiles se evita el "gran diseño previo". Pero no se evita el diseño: sólo que éste es más "liviano", y puede ir mejorando a lo largo de las iteraciones. El diseño de lo que queremos construir debería surgir de los casos de uso que relevamos. Acá nos vamos a saltear esa parte, para ir directamente al diseño.

Vamos a desarrollar un ejemplo: implementar una pila. Queremos escribir una clase, cuyas instancias funcionen como una pila de elementos. Para eso, esperamos que haya métodos Push (agregar un elemento) y Pop (obtener y retirar el elemento tope de la pila). También quisiéramos saber, antes de hacer un Pop, si la pila está vacia: tendremos una propiedad booleana IsEmpty.

Luego de discutir cuál es la funcionalidad deseada, llegamos a esta lista de tests:

- Crear Stack y verificar IsEmpty
- Push un elemento y IsEmpty es falso
- Push un elemento, Pop, IsEmpty en verdadero
- Push un elemento, igual al siguiente Pop
- Push tres elementos, Pop de los tres, verificar orden
- Pop de una Stack vacía (que genere InvalidOperationException)
- Push de tres items, Pop de uno, IsEmpty en Falso

Esta lista fue elegida para ir explicando paso a paso TDD. No siempre tenemos una lista completa o pensada de lo más simple a lo más complejo. Pero es importante recordar: esta lista ES la especificación de lo que esperamos de la pila. Por ejemplo: ¿qué pasa si hacemos Pop en una stack vacía? ¿qué esperamos de hacer Push(a), Push(b), y luego Pop()? Aunque algunos de los tests parecen simples, no están de más: van dejando explícitos ejemplos de uso del software que estamos construyendo.

Tampoco una lista de tests está "grabada en piedra". Si descubrimos nuevos casos de uso, nuevas formas de consumir el software, o nueva conducta a implementar, ¿qué hacemos? No escribir código, sino primero: escribir el test.


refactorizamos la implementación para usar una List de .NET, en lugar de Stack. Hacemos este cambio para mostrar que podemos modificar la implementación interna, y estar seguros de que todo sigue funcionando porque hemos escrito los tests correspondientes.

También en el este ejemplo refactorizamos el tests, apareciendo por primera vez el atribute [TestInitialize].



he  aqui  el ejemplo en youtube


luego de llegar a un test en verde(exitoso), no dejamos el código ahí, sino que lo "refactorizamos", lo revisamos y mejoramos.

Primera cuestión: ¿qué es este paso? Es cambiar el código del software que estamos construyendo, SIN AGREGAR funcionalidad. No cubrimos un test nuevo, ni cambiamos los colores de ningún tests. Todo lo que estaba en verde, sigue estando en verde. Ejemplos de refactorizar:
- Cambiar el nombre a un método, por uno más descriptivo.
- Por lo mismo, cambiar el nombre de un argumento o variable.
- Si un método es muy largo, partirlo en métodos más chicos, concentrados cada uno en un caso.
- Si encontramos código repetido, ponerlo en un solo método.
- Mejorar el algoritmo empleado internamente para resolver un problema.

Segunda cuestión: ¿por qué ese paso? Alguien podría preguntar: ¿por qué no hacerlo bien desde el principio? Recordemos, al seguir TDD vamos construyendo el código que resuelve nuestros tests, nuestros mini-casos de uso de desarrollo. Lo importante, en los primeros pasos, es conseguir pasar el test. Cuando tenemos un test en rojo, agregamos el menor código posible para pasarlo a verde. Tratar de hacer más, es como tratar de adivinar el futuro: se puede hacer, pero no siempre será productivo o acertado. De alguna manera, TDD refleja una actitud de avanzar con "baby steps", pasos de bebé. En lugar de tratar de hacer todo, y todo bien desde el principio, vamos avanzando de a pasos.

De ahí que, al conseguir el test en verde, nos concentramos en eso. No nos importa la claridad o buena organización del código que acabamos de construir. Ese paso no se concentra en todas las buenas prácticas, sino en la primera buena práctica: que el código que escribamos cumpla con lo que esperamos de él.

Tercera cuestión: ¿no lo podemos hacer después? Y mientras, seguir con otros tests. A veces, uno se ve tentado a seguir este camino. Pero el problema es que si seguimos armando tests, y haciéndolos pasar a verde, sin refactorizar, estamos acumulando "deuda técnica": estamos haciendo crecer el código sin refactorizar, y cuando nos toque mejorarlo, será demasiado trabajo a encarar. En vez de dejar un crecer un problema, el tener la refactorización ahora, antes de avanzar con otro test, hace que el código que vayamos armando esté en buena forma, la mayor parte del tiempo. Al confirmar nuestro código en el repositorio, podemos confiar en que es "buen" código: código que no sólo pasa los tests, sino código escrito que exhibe una buena salud.



como fracasar con la pruebas  unitarias

En problema aparente era la calidad de las pruebas, pero en realidad, el problema de fondo es la estrategia de hacer las pruebas luego de terminado el código. Por lo general, los programadores escriben piezas de código las cuales, para probarlas, son ejecutadas manualmente varias veces mientras que con el depurador se recorren línea por línea los algoritmos. Una vez que la feature está lista, quieren escribir algunas pruebas pero apenas de empezar se dan cuenta que el código que han escrito no es fácilmente testeable, no contempla la posibilidad de inyectarle las dependencias y probablemente han utilizado muchos de lo “enemigos de las pruebas unitarias”.
Aquí el programador puede tomar un de los siguientes caminos:



  1. Refactorizar el código para volverlo testeable.
  2. Probarlo como está, es decir, si el código toma valores de una tabla de la base de datos, pueden ponerle esos valores en la tabla al iniciar la prueba.
  3. No probarlo en absoluto. Esta es (Test-Never)
Estas decisiones no son libres ya que hay ciertos condicionamientos:





  1. Queda poco tiempo. Todo lo que se pudo ahorrar en depuración ya se perdió y ahora no solo se trata de escribir las pruebas sino que hay que refactorizar algo que “ya está andando” para recién luego escribir las pruebas.
  2. Probablemente esa refactorización no sea algo tan sencillo. Es probable que haya que modificar algo más de código que solo el propio. Esto ocurre cuando hay que lidiar con los métodos estáticos y otras malas yerbas ya presentes en el proyecto.
  3. El resto del equipo ya se ha encontrado en este dilema y la decisión que han toma es un antecedente de peso en la cultura del equipo.
Cual de los caminos toma el programador depende de muchos factores pero lo malo del caso es que ninguna de las tres alternativas conduce a algo bueno. Veamos por qué:
En el primer caso, se consume mas tiempo que el que se hubiese requerido si el código se hubiese hecho testeable desde el principio mediante TDD. Es probable que el programador vea esto como una pérdida de tiempo ya que su código “funciona” pero él tiene que modificarlo para “cumplir” con algo, llámese cobertura de código, número de pruebas, etc.
En cuanto a la segunda alternativa, la de probar sin modificar el código, es sin dudas un camino para realizar pobres pruebas de integración. Solo hay que hacerlas y esperar algo más de un año para ver el daño que que hacen al proyecto, cuanto cuestan y cuán poco sirven.
La tercera opción es la más coherente con el modo de desarrollo que se ha tomado pero es sin dudas una estafa. Si se ha estimado el tiempo necesario para codificar las pruebas y si ha comprometido con el equipo ha escribir pruebas para el código propio y pero no se lo lleva a cabo, entonces hay que sincerar la situación.
No interesa que tan buen programador sea, si no se escriben la pruebas interactivamente con el código se llegará tarde o temprano a esta situación.

En la terminología de TDD y testing en general, aparece la expresión Software Under Test, y su sigla SUT. ¿A qué refiere?

Cuando tuvimos un test escrito para probar la suma en el ejemplo de la calculadora, lo que estábamos poniendo a prueba no era TODOS los métodos de la clase Calculator, sino uno en particular, el método Add. Y así pasó con otros tests y otros ejemplos que vimos: muchos tests se concentran en probar una parte de lo que estamos construyendo. Esa parte en la que ponemos foco en un test, es lo que se llama SUT, Software Under Test (software bajo prueba)

Software Under Test

En la figura de arriba, el Unit1 podría ser el método Add de nuestra clase Calculator. Unit2 podría ser el método Subtract. Habrá algunas pruebas que involucren llamar a varias partes del software. Y hasta un test puede probar el software completo. Se usan menos las siglas MUT (Method Under Test), CUT (Class Under Test), AUT (Application Under Test).

Los ejemplos que hemos visitado hasta acá, han sido sencillos. En aplicaciones más grandes, uno no sólo prueba una parte del software que está construyendo. Suele suceder que éste, a su vez, consuma otras clases e instancias. Esas partes que NO estamos probando, sino que son usadas por SUT, se denominan Depended-On Component, DOC:

DOC

Ejemplo: puede que lo que tenemos que probar es una instancia de una clase del dominio de negocio, que tiene que aprobar o no un crédito para un cliente. Estamos probando que el algoritmo de aceptación del crédito funcione como esperamos. Este objeto necesita de la ayuda de otros objetos, por ejemplo, para consultar la base de clientes y sus movimientos. Esos objetos no son el foco de nuestra prueba, pero son necesarios para que el SUT funcione. Tendremos que estudiar cómo armamos los test en estos casos. Una alternativa a estudiar es tener objetos DOC que simplemente simulen y ayuden al SUT. Aplicado al ejemplo: podríamos darle al SUT, en lugar de los objetos DOC que acceden a la base de datos, otros que simplemente le entreguen datos que ya tenemos preparados en memoria. Veremos a qué se llama Stub y qué es Mock. Pero por ahora, basta tener en cuenta el problema y la nomenclatura de SUT y DOC.



 xUnit: una familia de software (NUnit, JUnit, SUnit, DBUnit, etc...) que se escribieron para ayudar a automatizar la ejecución de tests.

¿Qué características tienen estos programas?

- Muchos están implementados en lenguajes orientados a objetos (Smalltalk, Java, lenguajes .NET como C#, VB.NET)

- Permiten especificar un método de test (en nuestros ejemplos, con el atributo [TestMethod])

- Tienen soporte de métodos de aserciones (como el Assert.AreEqual que usamos)

- Formar conjuntos de tests para ejecutar en grupo (en nuestro caso, en clases y proyectos)

- Ejecutar uno o más tests, reportando los resultados (ya sea en la IDE, o en ejecución desde la línea de comando; produciendo un reporte visual o algún resultado en archivos para ser analizado)





TDD para codigo que maneja Bases de Datos 
como testear mejor los repositorios, o al menos de una formas mas sencilla. Esta vez hice 2 clases bases para eso:

- BaseMemoryRepository.cs: Es una clase abstracta que tiene 2 tipos genericos (). El TEntity indica cual es la clase que se debe persistir, por ejemplo puede ser un simple Customer, el TIdentity es el tipo de Id.

Si el TIdentity especificado es del tipo numerico (Int16, 32 ó 64) o GUID, la misma clase se encarga de la generacion automatica, pero si se quiere usar algo muy complicado la unica solucion es hacer un override del metodo object GetNextId().

De esta forma, un repositorio simple quedaria asi:

public class CustomerRepository : BaseMemoryRepository,
                                  ICustomerRepository
{
    protected override int GetEntityId(Customer ent)
    {
        return ent.Id;
    }

    protected override int GetEntityId(Customer ent)
    {
        return ent.Id;
    }

    protected override void SetEntityId(Customer ent, int ident)
    {
        ent.Id = ident;
    }
} 

- CustomerRepositoryTests.cs: Esta clase es un poco mas compleja de usar que la anterior. Provee la funcionalidad para realizar CRUD todo junto, si bien no es lo mejor que se puede hacer en TDD, normalmente testear las operaciones basicas es resuelto de la misma forma. Por ejemplo si se usa un ORM lo mas probable es que funciona todo o no funciona nada.
Es importante implementar 4 metodos en esta clase, estos se encargan de:
- Crear un objeto de Testing
- Devolver el Id de una entidad, necesario por el desconocimiento de cual es la propiedad Id de un objeto
- Validar si el id de un objeto es valido; una vez que se crea un objeto se espera que un nuevo Id se le asigne, por eso con validar solamente que sea mayor a 0 alcanza en la mayoria de los casos.
- El Test :) (que tiene que llamar a base.BasicOperationsTest())

Aca va un ejemplo:

[TestFixture]
public class CustomerRepositoryTests : BaseRepositoryTests
{
    public override void BuildTestEntity(out Customer e)
    {
        // Tengo que crear un nuevo Customer y configurar
        // informacion de prueba 
        e = new Customer();
        e.Name = "Name";
        e.Gender = Gender.Male;
        e.BornDate = DateTime.Now;
    }

    public override object GetEntityId(Customer ent)
    {
        return ent.Id;
    }

    public override bool IsValidEntityId(Customer entity)
    {
        // Una validacion simple 
        return entity.Id > 0;
    }

    [Test]
    public override void BasicOperationsTest()
    {
        // Aca hago el trabajo real! 
        base.BasicOperationsTest();
    }
}
Y listo, una vez que implementamos esas 2 clases por repositorio ya tenemos todos los test simples...



Reflexiones sobre los problemas del desarrollo orientado a pruebas

El autor comienza exponiendo que no está demostrado por la comunidad científica que TDD aporte mejoras significativas en el desarrollo del software. Como ejemplo cita a un par de párrafos de Maria Siniaalto en su artículo Test-Driven Development: empirical body of evidence. De esos dos, me quedo con el siguiente:
The empirical evidence on the practical use of TDD and its impacts on software development are still quite limited.
[La evidencia empírica en el uso práctico de TDD y su impacto en el desarrollo del software esta, todavía, muy limitada]
Con este soporte, pasa a dar su opinión que defenderá en el resto del texto:
I mention this first because I’ve concluded that not only is TDD not useful for me but I don’t think it’s a generally useful technique
[Menciono esto primero porque he concluido que no sólo TDD no es útil para mi, sino que no creo que sea una técnica útil en general]
No hace mucho, en la lista de correo de TDD en español (en la cual os invito a participar si estáis interesados en este tema) hablamos de lo mismo debido a otro articulo diferente. Mi punto sigue siendo el mismo que en aquel entonces (de hecho, he reutilizado algunas frases). Un estudio para ver si TDD mejora o no la calidad, rendimiento, etc., tiene que tener muchísimas variables en cuenta. Se me ocurren tres escenarios:
  • Proyectos distintos (con y sin TDD), gente distinta. La comparación no es posible porque la gente es distinta, los proyectos son distintos, los equipos son distintos, los comportamientos son distintos, etc.
  • Mismos proyectos (con y sin TDD), misma gente. Si ponemos primero a gente en un proyecto sin hacer TDD y se estudia, y luego se repite el estudio con la misma gente haciendo TDD, tampoco es posible hacer una buena comparación porque los individuos ya tienen conocimiento y experiencia en el proyecto.
  • Proyectos distintos (con y sin TDD), misma gente. Igualmente habría muchas variables dentro de los proyectos que podrᅵan influir en el resultado del mismo (complejidad, asuntos personales, ambiente…).
Según lo veo yo, para hacer un estudio de estas características se necesitan un número suficientemente grande de gente y proyectos para que sea estadísticamente significativo (y eso es mucho dinero y mucho tiempo). No creo que vaya a pasar y por tanto siempre habrá unos artículos dónde salga que TDD es mejor y otros en los que se diga que TDD es peor ya que, como se ha dicho, hay muchos otros factores influyen en el resultado.
Lo importante para mi no es que haya artículos de investigación que “demuestren” las maravillas o la perdida de tiempo que supone hacer TDD. Lo que a mi me importa es lo que yo veo y siento cuando hago TDD y lo que, por mi experiencia, veo en equipos e individuos cuando hacen TDD. Personalmente, a mi me sirve para hacer mejor código, más claro y más testeable. Mi código es mejor, mi actitud es mejor, me obligo a pensar más en las pruebas, tengo más confianza en que lo que he hecho funciona y, además, disfruto haciéndolo.
En el artículo mencionado al principio, el autor dice que TDD no da confianza en que el código funcione. La explicación es que en TDD no se pueden añadir pruebas que no hagan fallar código:
TDD by itself cannot give you that confidence because it excludes the idea of adding tests which are expected to pass
[TDD por sí mismo no puede darte esa confianza porque excluye la idea de añadir pruebas que se espera que pasen]
Estoy completamente en desacuerdo. Es más, no estoy del todo seguro de que, incluso siendo purista, no se puedan añadir pruebas que no hagan fallar el código. Robert C. Martin (Uncle Bob) escribe en su artículo “Las tres reglas de TDD“, la siguiente regla número dos: “You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures“. Esta frase se puede interpretar como que una vez que has escrito una prueba que haga fallar el código, no esté permitido escribir ninguna otra prueba más, pero eso no tiene que significar que todas las pruebas que se escriban deban fallar en un principio. Es más, Kent Becken su libro Test-Driven Development by example habla de sólo dos reglas fundamentales antes de presentar más:
  1. Write new code only if an automated test has failed [Escribe nuevo código sólo si una prueba automática ha fallado]
  2. Eliminate duplication [Elimina la duplicidad]
Ninguna de estas reglas supone una contradicción al escribir pruebas que pasen. Si lo miramos desde un punto de vista práctico, no hacemos más que asegurarnos de que ese caso pasa, ¿qué tienes esto de malo?, ¿por qué deberíamos borrarlo o no usarlo si pasa?, ¿por qué debería estar prohibido?. Cuando escribimos código para hacer pasar un test, escribimos el código más simple que podemos pensar para hacer pasar la prueba, sin embargo, esto no tiene por qué conllevar que ese “código mínimo” solamente hace pasar ese test específico. Algunas personas que hacen TDD dicen que lo que habría que hacer es “romper el código” artificialmente para ver cómo la prueba falla y después “arreglarlo” para verla pasar. En mi opinión esto es una pérdida de tiempo y ganas de hacer que una buena práctica parezca una religión. (Incluso el diagrama de flujo en la wikipedia, no ve contradicción en escribir una prueba que pase :-) )
El autor también habla de que TDD no considera el peor caso o casos límite. ¿Cómo?. Claro que no, esto es puramente dependiente del programador (igual que lo es si hacemos pruebas al final), de lo cuidadoso que sea con las pruebas y de cuanto piense en los casos que necesita. No obstante, hay una gran ventaja al hacer TDD y es que para que escribir el código tienes que escribir las pruebas antes y eso te garantiza, al menos, cierto número de casos (¿cuantos se garantizan al hacer pruebas al final?). Obviamente, hay que pensar y esforzarse, eso no viene gratis por el hecho de TDD. Sin embargo, en mi experiencia es más fácil olvidarse de un test cuando ya tienes el código escrito que cuando todavía está por escribir y tienes que pensar en casos y comportamientos. Habla también del rendimiento y de cómo TDD no se centra en ello. Estoy de acuerdo en que TDD no es la mejor aproximación a la programación de algoritmos pero eso no quita para que no se pueda hacer. Igual que se hacen otro tipos de pruebas, se pueden añadir pruebas de rendimiento y mejorarlo en la fase de refactorización sin cambiar el comportamiento. ¡Para eso tenemos las pruebas!
Hay algunas cosas más de las que habla el autor, pero estas eran para mi las más importantes y en las que me quería enfocar y rebatir. Hay una idea que sí que nunca había oído antes que me ha llamado mucho la atención y en la que creo que merece la pena reflexionar:
If I write the tests first, I also worry that I’ve overfit my code to the tests. This is a problem that happens in statistical modelling. Given any set of data points, I can fit them to a model. The next question is, is the model valid and useful? The way to check is to use them to make predictions, and see how well it matches reality. This in turn means testing the model with data which wasn’t used to make the model.
[Si escribo pruebas primero, también me preocupo de que he me he pasado dando forma al código con las pruebas. Este es un problema que ocurre en modelos estadísticos. Dado cualquier conjunto de puntos de datos, puedo encontrar un modelo que le corresponda. La siguiente pregunta es, ¿es el modelo válido y útil?. La manera de verificar esto es usar el modelo para hacer predicciones y ver cómo de bien refleja la realidad. Esto significar probar el modelo con datos que no han sido usado para crear el modelo]
En principio, esa es una de las grandes ventajas de TDD, el modelado del código mediante las pruebas, pero, ¿es posible que debido a las pruebas estemos haciendo un modelo que se ajuste a las pruebas pero no al comportamiento general? Es posible y realmente merece la pena mirar los modelos estadísticos para entenderlo un poco mejor (lo tengo pendiente ya que la estadística la tengo muy olvidada) (supongo que este es el problema de hacer modelos de dominios infinitos con un número finito de datos). Sin embargo, creo que es obvio que TDD no es la panacea y que si usamos TDD todavía necesitamos usar otras prácticas. El código, después de todo, debe ser sujeto a pruebas de sistema, de estrés, de rendimiento, de exploración que, por otro lado, están fuera del modelo bajo el cual se ha escrito el código.
Como resumen, decir que creo que es estúpido el tratar a TDD como una religión. Pero es igualmente estúpido el tratar de ser “anti-TDD” como religión. No creo que es lógico decir que TDD es la solución a todos mis problemas, al igual que no es lógico decir “TDD es una práctica que no sirve para nada si usas otras prácticas”. Esto no llega a ningún lado y el contexto influye en las prácticas que hay que tomar y cuando utilizar una u otra. En mi experiencia, he visto gente y equipos que han mejorado mucho (sobre todo en número de errores bugs) al empezar a hacer TDD mientras que otros han hecho el mismo código malo con el añadido de que un montón de pruebas horribles. También he visto equipos con gente muy metida en tests (haciéndolos después del código) que al tomar TDD en práctica no han notado mejoría en número de bugs o en rendimiento. Al final, lo más importante es tener buena gente en el equipo que tengan actitud profesional, buenas aptitudes y ganas de mejorar.



links

Rhino-mocks  una  herramienta  para facilitar el testing en  .net


Test-Driven Development in Microsoft .NET


James W. Newkirk, Alexei A. Vorontsov

Explica desde el comienzo lo que es TDD, para un programador que no conoce del tema. Lo interesante es que usa .NET, Visual Studio y NUnit para los ejemplos.

Incluye un apéndice donde comenta cómo usar NUnit. 
Test-Driven Development by Example

Test-Driven Development by Example



Kent Beck

Desarrolla varios ejemplos, incrementalmente, mostrando cómo usar TDD sobre ellos.

En la primera parte, plantea un problema simple: modelar Moneda, con distintos tipos de moneda, conversiones, etc.

En la segunda parte, toma el desafío de desarrollar un xUnit, una herramienta para TDD, usando justamente TDD.

En la tercera, enumera y explica patrones para TDD.
xUnit Test Patterns

xUnit Test Patterns



Gerad Meszaros

Una lista y explicación de más de 50 patrones que los desarrolladores usan al escribir tests. La gran mayoría se aplica en proyectos con TDD.
xUnit Test Patterns

Diseño Agil con TDD

Carlos Blé Jurado, Juan Gutiérrez Plaza, Fran Reyes Perdomo y Gregorio Mena

Pueden bajarlo desde
http://www.dirigidoportests.com/el-libro
DisenoAgilConTDD

Lo interesante es que abarca más allá de TDD: trata de desarrollo ágil en general, ATDD (test de aceptación), Diseño orientado a objetos, Principios S.O.L.I.D.



Demystifying Extreme Programming: Test-driven programming http://www.ibm.com/developerworks/java/library/j-xp042203/index.html
Jess Chadwick: Coder: What’s a “Unit Test”?http://jesschadwick.blogspot.com/2009/11/what-unit-test.html
Gojko Adzic » FitNesse book now free onlinehttp://gojko.net/2009/12/07/fitnesse-book-now-free-online/
What is Extreme Programming? | xProgramming.com
http://xprogramming.com/xpmag/whatisxp
Introduction to Test Driven Design (TDD)
http://www.agiledata.org/essays/tdd.html
An intro into Test Driven Development with JUnit4http://www.turnleafdesign.com/?p=260
Gojko Adzic » Mockito in six easy examples
http://gojko.net/2009/10/23/mockito-in-six-easy-examples
Using .NET: Tame Your Software Dependencies for Flexible App http://msdn.microsoft.com/en-us/magazine/cc337885.aspx
Welcome to TDD Problems! (TDD Problems)http://sites.google.com/site/tddproblems
How to measure programmer productivity using TDD Katashttp://5whys.com/blog/how-to-measure-programmer-productivity-using-tdd-katas.html
How test-driven development works (and more!)
http://www.jbrains.ca/permalink/285
Writing meaningful, self-documenting, behavior-oriented tests http://www.clariusconsulting.net/blogs/kzu/archive/2009/10/01/171565.aspx
How-To: Using the N* stack, part 1 " BASICly everything http://jasondentler.com/blog/2009/08/how-to-using-the-n-stack-part-1
davenicolette - TDD Randori and Fishbowl http://davenicolette.wikispaces.com/TDD+Randori+and+Fishbowl
Autumn of Agile Screencast Serieshttp://www.autumnofagile.net
The Disco Blog " Blog Archive " Is BDD TDD done right? http://thediscoblog.com/2007/08/28/is-bdd-tdd-done-right
Growing Object-Oriented Software, Guided by Tests
http://www.mockobjects.com/book
Daniel Cazzulino's Blog : Funq: screencast series on how to building a DI container using TDDhttp://www.clariusconsulting.net/blogs/kzu/archive/2009/02/02/116399.aspx
Professional Software Development » Java Unit Testing Tutorial
http://www.basilv.com/psd/blog/2009/java-unit-testing-tutorial

TDD para C# usando el  framework de  pruebas  oficial de  visula studio.net 2010  Unit test Framework

Cuales  son los atributos que maneja La clase  ASSERTS de  mvs Unit Test Framework 

Convertir Java en .net o .net en java?

pues  si, por alguna  razon  (ideologica  creo yo)   han surgido varios  proyectos  para transformar codigo de .net a java  y viceversa
aqui dejo algunos enlaces para los  interesados

link1
link2
link3

mezclar lenguajes en .net

obtenido de aqui

Una de las cosas más reseñadas de ASP.NET 2.0 es que es capaz de gestionar páginas ASPX creadas en diferentes lenguajes todas al mismo tiempo. Es decir, puedes tener una página o un control escritos en C# y mezclarlos con otros escritos en VB.NET o J# sin problema. Incluso puedes depurarlos todos juntos desde Visual Studio, saltando sin problemas de un lenguaje a otro.

Ahora bien, desde mi punto de vista esto no es demasiado interesante. Es raro que reutilices una página de una aplicación a otra y si escribes una aplicación completa desde cero normalmente lo haces en el mismo lenguaje. Hombre, pensando en equipos de desarrollo donde varios programadores contribuyen al mismo proyecto y trabajan en lenguajes distintos es muy interesante, pero vamos...

Normalmente lo que compartes entre aplicaciones son controles o bibliotecas de funciones, las cuales normalmente deberías compilar en sus propios ensamblados y listo. Ya no tendrías que mezclar lenguajes ahí.

Otra posibilidad es que tengas un módulo o una clase escritas en tu carpeta App_Code de una aplicación y quieras reutilizarlas en otra. Normalmente lo que tendrías que hacer es simplemente copiarlas a la carpeta App_Code de tu proyecto nuevo y ya estaría. El problema es que App_Code no permite mezclar código de varios lenguajes dentro de ella. Así, si copias un archivo .vb a una carpeta App_Code que ya contiene algún archivo .cs verás que al ejecutar la aplicación el compilador se queja y te dice que no puedes mezclar lenguajes en App_Code.

Entonces ¿se pueden o no se pueden mezclar lenguajes?

Pues la respuesta es que sí, pero no directamente.

Cuando el compilador de ASP.NET compila de forma automática el contenido de la carpeta App_Code lo que hace es generar una DLL única para todas las clases que allí estén y para ello usa el compilador del lenguaje en el que estén escritas. Si están en más de un lenguaje no sabe a qué compilador llamar y por eso se queja.

Para resolver este asunto podemos echar mano de un "truco" que consiste en indicar al compilador cómo queremos compilar nuestro código existente en App_Code. En la sección de web-config se puede indicar cuántas DLL de código queremos obtener a partir de App_Code y cómo están éstas organizadas. Cada ensamblado generado se corresponderá con una carpeta dentro de App_Code y se llamará al compilador para dicha carpeta.

De este modo podemos guardar en una carpeta todo el código en VB, en otra carpeta el código en J# y dejar en el raíz las clases escritas en nuestro lenguaje principal (en mi caso C#). La estructura podría quedar así:



Está claro: cada lenguaje en su carpeta.

Sin embargo si ahora ejecutamos la aplicación veremos que se vuelve a producir un error. Sigue sin funcionar.

El problema es que todvía nos hace falta ajustar las carpetas de código en web.config, de la siguiente manera:







Fíjate en que no se indica qué lenguaje se usa para compilar cada carpeta (eso lo determina automáticamente el compilador) sino que se va a generar un ensamblado por cada carpeta y por lo tanto se puede usar un compilador diferente para cada una de ellas. De ahí que se pueda mezclar el código.

Ahora si ejecutas la aplicación verás que todo funciona sin problemas y puedes depurar todos a la vez etc...

Un par de observaciones:

1.- Si te fijas en la imagen de arriba, la de las carpetas, verás que éstas tiene un aspecto "normal", es decir, son amarillentas y no tienen distintivo especial alguno. Tras haber añadido las líneas anteriores al web.config si te vuelves a fijar verás que su aspecto es el mismo que el de la carpeta App_Code ya que van a ser compilados:



2.- ¿Por qué funciona a mezcla de lenguajes en las páginas? Porque cada página en el nuevo modelo de compilación de ASP.NET 2.0 (que a mi no me acaba de convencer) se compila en su propio ensamblado de manera individual, de ahí que pueda estar cada una de ellas en su lenguaje. Lo que no se puede hacer es mezclar lenguajes en la misma página, claro.

domingo, 1 de agosto de 2010

diseño por contrato: construyendo software confiable

El diseño por  contrato es una metodologia  de  programacion  que  permite  asegurar que  cada  funcion implementada se  confiable.

El diseño por contrato está basado en “viejos” conceptos de verificabilidad de programas de Hoare (An axiomatic basis for computer programming, 1969) y Mills (How to write correct programs and know it, 1975).


El Diseño por contrato es una técnica valiosa que siempre se debe emplear cuando se programa. Es particularmente útil para la construcción de interfaces claras.
Sólo Eiffel maneja afirmaciones como parte de su lenguaje, pero desafortunadamente Eiffel no es un lenguaje de amplía difusión. La adición de mecanismos a C ++ y Smalltalk que manejen algunas afirmaciones es directa. Es mucho más engorroso hacer algo similar en Java, pero es posible.
El UML no habla mucho acerca de las afirmaciones, pero se pueden usar sin problemas. Las invariantes son equivalentes a las reglas de condición en los diagramas de clase y se deberán usar tanto como sea Posible. Las precondiciones y poscondiciones de operación se deben documentar con las definiciones de las operaciones.

En Estados Unidos "Design by Contract" es una marca registrada, por lo que muchos desarrolladores lo llaman Programming by Contract (Programación por Contratos), o Contract Programming (Programación Contractual), o contract-first development (desarrollo con contrato primero).
La idea central de DbC es una metáfora sobre cómo interactuan los elementos de un sistema de software para colaborar entre si, basándose en obligaciones y beneficios mutuos. La metáfora proviene del mundo de los negocios, en donde un "cliente" y un "proveedor" firman un "contrato" que define por ejemplo:Una mejor comprensión de la programación orientada a objetos. Esto se debe a que el propio prototipo de un recurso de una clase es también parte del contrato que éste ofrece. Mediante razonamientos de este tipo, por ejemplo, se justifica el comportamiento covariante de los tipos de retornos de los métodos en Eiffel.
beneficios
  • Menos errores en el código gracias a una mejor representación de las especificaciones.
  • Un sistema efectivo para detectar errores.
  • Una forma práctica de documentar el código al mismo tiempo en que se programa.
  • Una imbricación eficaz con los mecanismos de herencia y reutilización.

  • el proveedor debe brindar cierto producto (obligación) y tiene derecho a que el cliente le pague una cuota (beneficio).
  • el cliente paga una cuota (obligación) y tiene derecho a obtener el producto (beneficio).
  • ambas partes deben satisfacer ciertas obligaciones, como leyes y regulaciones, que se aplican a todos los contratos.
De manera similar, si una rutina de una clase en Programación Orientada a Objetos brinda cierta funcionalidad, podría:
  • imponer ciertas obligaciones que se garanticen por cualquier módulo cliente que la invoque: la precondición de la rutina- una obligación del cliente, y un beneficio para el proveedor (la rutina en si misma), ya que la libera de tener que gestionar casos por fuera de la precondición.
  • garantizar cierto comportamiento a la salida: la postcondición de la rutina - una obligación del proveedor, y obviamente un beneficio para el cliente.
  • mantener cierta propiedad, asumida al momento de la entrada y garantizada a la salida: las invariantes de la clase.
El contrato es la formalización de estres obligaciones y beneficios. Se podría resumir al Diseño por Contrato por estas tres preguntas que el diseñador del compomnente debe preguntarse:
  • ¿Qué espera?
  • ¿Qué garantiza?
  • ¿Qué mantiene?
Muchos lenguajes brindan facilidades para hacer verificaciones como estas. Sin embargo, DbC considera que los contratos son cruciales para crear software correcto, y que deben ser parte del proceso de diseño del software. De hecho, DbC fometa escribir primero las asersiones.
La noción de contrato se extiende hacia el nivel de los métodos/procedimientos; el contrato para cada método normalmente contendrá la siguiente información:
  • tipos y valores aceptados y no aceptados para la entrada, y su significado.
  • tipos y valores de retorno, y su significado.
  • tipos y valores de error y condiciones de excepción, que puedan ocurrir, y su significado.
  • efectos secundarios.
  • precondiciones, que las subclases pueden debilitar (pero no reforzar)
  • postcondiciones, que las subclases pueden reforzar (pero no debilitar)
  • invariantes, que las subclases pueden reforzar (pero no debilitar)
  • (raramente) garantías de rendimiento, por ejemplo, de tiempo o espacio utilizado.
Cuando se utilizan contratos, el código del programa por si mismo nunca debe intentar verificar las condiciones del contrato; la idea es que el código debe "fallar duro", siendo la verificación del contrato la red de contención. Este concepto de "fallar duro" de DbC simplifica el debug del comportamiento del contrato, ya que el comportamiento requerido para cada rutina está claramente especificado.
Nunca se deben violar las condiciones del contrato en la ejecución del programa; por lo tanto se lo puede dejar activado para debug, o quitado completamente del código productivo por motivos de rendimiento.
Todas las relaciones de clases son entre clases Cliente y clases Proveedor. Una clase Cliente está obligada a hacer las llamadas al Proveedor
La Prueba Unitaria verifica módulos aislados, para comprobar si cumplen con el contrato asumiendo que los demás proveedores cumplen el suyo. La Prueba De Integracion comprueba que varios módulos funcionan correctamente entre si. El Diseño por Contrato también facilita la reutilización de código, ya que el contrato para cada pieza de código está completamente documentado. Los contratos de un módulo también pueden verse como una forma de documentación para el comportamiento del módulo.




Hay varias herramientas que facilitan la implementación de DbC en en software.
Algunas herramientas de DbC para Java:


aqui un texto que  describe  claramente su  utilidad. producido por la UNAM por cierto.
aqui lo que dice la wikipedia en ingles
esto es lo  que nos dice  la wikipedia
aqui un texto en ingles  sobre el lengueje Eiffel que es el  precursor u origen del "Diseño por Contrato"
diseño por contrato en C# usando Code Contracts
aqui el enlace a "Code Contract Tools" herramienta  necesaria  para que  funciones en vs net 2010
ejemplo en c#
otro ejemplo en c#
introduction  to Code Contracts

porque y cuando usar interfaces

a continuacion una  serie  de  articulos  que  he e ncontrado en la  red  y  que  permiten entender con mayor claridad el uso de las  interfaces  en la  programacion  para  mejorar  la  calidad de  codigo desarrollado.


este articulo lo encontre en este  foro:http://www.forosdelweb.com/f68/duda-con-interface-php5-440249/
explica claramente  el concepto de interfaz

Interfaces

Una interface es una variante de una clase abstracta con la condición de que todos sus métodos deben ser asbtractos. Si la interface va a tener atributos, éstos deben llevar las palabras reservadas static final y con un valor inicial ya que funcionan como constantes por lo que, por convención, su nombre va en mayúsculas.
interface Nomina
{
 public static final String EMPRESA = "Patito, S. A.";
 public void detalleDeEmpleado(Nomina obj);
}

Una clase implementa una o más interfaces (separadas con comas ",") con la palabra reservada implements. Con el uso de interfaces se puede "simular" la herencia múltiple que Java no soporta.

class Empleado implements Nomina
{
   ...
}
 
una Interfce no puede ser instanciada 





En este ejemplo, la clase Empleado tiene una clase padre llamada Object (implícitamente) e implementa a la interface Nomina, quedando el diagrama de clases de la siguiente manera:


La clase que implementa una interface tiene dos opciones:
1) Implementar todos los métodos de la interface.
2) Implementar sólo algunos de los métodos de la interface pero esa clase debe ser una clase abstracta (debe declararse con la palabra abstract).



Referencias a Interfaces

Es posible crear referencias a interfaces, pero las interfaces no pueden ser instanciadas. Una referencia a una interface puede ser asignada a cualquier objeto que implemente la interface. Por ejemplo:
InstrumentoMusical instrumento = new Guitarra();
instrumento.play();
System.out.prinln(instrumento.tipoInstrumento());

InstrumentoMusical i2 = new InstrumentoMusical(); //error.No se puede instanciar

Extensión de interfaces

Las interfaces pueden extender otras interfaces y, a diferencia de las clases, una interface puede extender más de una interface. La sintaxis es:
interface nombre_interface  extends nombre_interface  , . . . {
    tipo_retorno nombre_metodo ( lista_argumentos ) ;
    . . .
}

Agrupaciones de constantes

Dado que, por definición, todos los datos miembros que se definen en una interface son static y final, y dado que las interfaces no pueden instanciarse resultan una buena herramienta para implantar grupos de constantes. Por ejemplo:
public interface Meses {
    int ENERO = 1 , FEBRERO = 2 . . . ;
    String [] NOMBRES_MESES = { " " , "Enero" , "Febrero" , . . . };
}

Esto puede usarse simplemente:
System.out.println(Meses.NOMBRES_MESES[ENERO]);

Las herencias de clases definen el "qué son" y las de interfaces agrupan clases que definen el "qué hacen".

Cuando agrupas por herencia hay una relación de parentesco, pero con las interfaces agrupas cosas que "hacen cosas similares" como una interfaz "imprimible" que todos tus objetos deberían implementar para poder pasar por la impresora (les estás diciendo de alguna forma que deben cumplir con un "contrato de implementación" para poder ser "imprimibles").

O hacer una interfaz "Pelable" para que la implementen las bananas, las manzanas y tu multiprocesadora. Esta última no es pariente de "frutas", pero puede hacer la misma acción que es "pelar".

Esto se relaciona con el polimorfismo, en el sentido que vas a tener un método de una clase, no sé (ya me estoy divagando mucho), cocinero, que pueda pelar cualquier cosa que le den, mientras estas sean "pelables".


Código PHP:
class Cocinero{
   public function 
pelar($elemento){
      
$elemento->pelar();
   }
}  
El "contrato de implementación" se hace cuando haces este "control de tipo":


Código PHP:
class Cocinero{
   public function 
pelar(Pelable $elemento){
      
$elemento->pelar();
   }
}  
Entonces solo pueden entrar los objetos que cumplan con la interfaz.

Si lo quieres ver de otra forma, se podría hasta decir que las interfaces son una extensión conceptual de la herencia (por lo que puse al principio).

Y finalmente, y esto es clave en entornos como Java, las interfaces permiten pasar del estilo de diseño "orientado a la implementación" a uno "orientado a la interfaz", donde todas las clases acceden a servicios a través de interfaces que son implementadas por clases concretas. Y al no depender de clases concretas (solo de entidades abstractas) nuestro diseño será más reutilizable que el anterior.



este articulo lo encontre en este  blog:http://chuidiang.blogspot.com/2005/10/por-qu-usar-interfaces.html

es muy ilustrativo

Por qué usar interfaces

Una pequeña explicación de por qué usar interfaces al programar. Aunque los ejemplos son java, la idea vale para cualquier lenguaje orientado a objetos.

Imaginemos que hacemos un programa que echa unas cuentas y va presentando en pantalla unos resultados. Puede ser algo como esto

void metodoConLasCuentas()
{
double valor=this.cuentas(datos);
System.out.println ("El valor es "+valor);
double valor2 = this.otrasCuentas(datos);
System.out.println ("El otro valor es " + valor2);
}

Ya está el programa. Se lo enseñamos a nuestro jefe, a nuestro cliente, a nuestro mejor amigo o al gracioso de turno y nos dice ... "Me gustaría poder sacar eso por impresora".

Nos ponemos manos a la obra y cambiamos nuestro programa

void metodoConLasCuentas()
{
double valor=this.cuentas(datos);
this.sacaPorImpresora ("El valor es "+valor);
double valor2 = this.otrasCuentas(datos);
this.sacaPorImpresora ("El otro valor es " + valor2);
}

Volvemos a enseñárselo al graciosillo y nos dice ... "Es que yo querría poder elegir sobre la marcha si lo quiero en pantalla o en impresora".

Nuevamente nos ponemos a tocar el código, pero esta vez algo más complejo

void metodoConLasCuentas()
{
double valor=this.cuentas(datos);

if (porImprsora==true)
this.sacaPorImpresora ("El valor es "+valor);
else
System.out.println ("El valor es "+valor);

double valor2 = this.otrasCuentas(datos);

if (porImpresora==true)
this.sacaPorImpresora ("El otro valor es " + valor2);
else
System.out.println ("El otro valor es " + valor2);
}

Nuevamente el gracios de ideas infinitas nos dice ... "Maravilloso, pero sería bueno poder guardar esos valores en base de datos".

Hartos de tocar el código decidimos implementar una solución que nos valga para todas las peticiones estúpidas del graciosillo. La solución es usar una interface. Hacemos nuestra interface así

interface Escritor
{
public void escribe (String cadena);
}

Ahora hacemos varias clases que implementen la interface, una clase Pantalla con el System.out.println, una clase Impresora con el sacaPorImpresora y una clase ABaseDeDatos con algo que sea capaz de meter esa cadena en la base de datos.

Nuestro código queda así de bonito ahora

void metodoConLasCuentas (Escritor escritor)
{
double valor=this.cuentas(datos);
escritor.escribe ("El valor es "+valor);
double valor2 = this.otrasCuentas(datos);
escritor.escribe ("El otro valor es " + valor2);
}

Llamando a nuestro método pasándole una clase Pantalla, sacará el texto por pantalla, con una clase Impresora, lo sacará por impresora y con un ABaseDeDatos lo guardará en base de datos.

Ya no tenemos que tocar nunca más nuestro código. Únicamente hacernos una nueva clase para la nueva idea del graciosillo y llamar a nuestro método con la nueva clase.

Este es un ejemplo tonto, pero si nos fijamos en java, hay multitud de sitios donde se usa.
  • Un JButton admite una interface ActionListener con un método actionPerformed(). El botón está hecho una única vez para siempre, y pasándole una Interface ActionListener, ese botón hace lo que queramos nosotros. Esta vez la gente de java eran los programadores y nosotros los graciosillos que queremos que el botón haga no sé qué.
  • Un JTable admite una interface TableModel. El TableModel es la interface y somos nosotros los que debemos rellenar los métodos. De esta forma un JTable hecho una sola vez es capaz de pintar cualquier tabla de datos, sin necesidad de tocarla por dentro. Únicamente tenemos que hacernos nuestra clase TableModel y pasársela al JTable.
  • Un Thread admite una interface Runnable. Esta clase Thread es capaz de hacer que se ejecute en un hilo aparte cualquier cosa que nosotros queramos. Únicamente hay que implementar el método run() de la interface Runnable y pasarle eso a la clase Thread.
  • etc, etc, etc.

Este otro texto sacado del msdn de microsoft: http://msdn.microsoft.com/es-es/library/3b5b8ezk(VS.80).aspx
lo complementa perfectamente


Cuándo se deben utilizar interfaces

Las interfaces son una herramienta de programación eficaz porque le permiten separar la definición de objetos de su implementación. La herencia de clases y las interfaces tienen ventajas e inconvenientes, y puede que al final decida utilizar una combinación de ambas en sus proyectos. Esta página y Cuándo utilizar la herencia le ayudarán a determinar qué enfoque es mejor para su situación.

Flexibilidad en la implementación

Existen otras razones por las que se podría desear utilizar interfaces en lugar de la herencia de clases:
  • Las interfaces admiten mejor las situaciones en las cuales las aplicaciones necesitan el mayor número de tipos de objetos no relacionados posible para proporcionar determinadas funciones.
  • Las interfaces son más flexibles que las clases de base, porque puede definir una única implementación que puede implementar interfaces múltiples.
  • Las interfaces son mejores en situaciones en las que no es necesario heredar una implementación de una clase de base.
  • Las interfaces son útiles en casos en los que no se puede utilizar la herencia de clases. Por ejemplo, las estructuras no pueden heredarse de las clases, pero pueden implementar interfaces.

POO: Interfaces, Dependencia de clases y Polimorfismo

La dependencia de clases es un concepto de la programación orientada a objetos que nos indica la relación existente entre dos clases. Como su nombre indica nos está diciendo que una clase depende de otra para realizar su funcionamiento.

Cuando trabajamos con clases es una buena práctica que una clase realice una única función. De manera que las clases que deban realizar funciones complejas estarán formadas a partir de una asociación de diversas pequeñas clases en las que delegará cada funcionalidad en concreto.

Este sería un ejemplo simple de una relación de asociación y de delegación entre clases:

Código :
package 
{
   public class Foro
   {
      private var _moderador:Zguillez;

      public function Foro()
      {
         _moderador = new Zguillez();
      }
      public function moderar():void
      {
         _moderador.moderar();
      }
   }
}


Aqui vemos como la clase "Foro" está delegando toda la funcionalidad del método "moderar" al objeto de clase "Zguillez", haciendo de esta manera que toda la implementación esté en esa clase dejando la clase principal más limpia y ordenada. Esto nos permite una mayor reutilización de nuestras clases.

El problema que nos encontramos aquí es que se ha creado una relación de dependencia muy grande entre estas dos clases. La clase "Foro" tiene una referencia directa a la clase "Zguillez" y necesita estrictamente de esa clase para funcionar.

De manera que si tuviésemos (por requisitos de la aplicación o por reutilización del código) que cambiar la implementación de la función "moderar" tendríamos que reescribir la la clase "Zguillez" para cambiar su implementación concreta o reescribir la clase "Foro" para delegar esa función a la clase "Zah" (por ejemplo). Con lo que hace estas clases poco reutilizables.

Polimorfismo


Esta dependencia la podemos romper gracias al Polimorfismo de las clases. El polimorfismo es la propiedad que tienen las clases de ocultar la estructura interna en una jerarquía de clases, implementando de manera diferente un mismo método. El polimorfismo nos permite que una identificación se refiera a diferentes entidades.

En este caso haremos que "moderador" se pueda referir tanto a "Zguillez" como a "Zah". Este polimorfismo lo podemos conseguir gracias a la herencia de clases o a la implementación de una Interface.

Definiremos una Interface que contendrá las funciones que estamos delegando a la clase asociada. En este caso sería así:

Código :
package 
{
   public interface IModerador
   {
      function moderar():void;
   }
}


Ahora asociaremos el objeto a la Interface en lugar de a una clase en concreto:

Código :
private var _moderador:IModerador;


Tendremos que hacer que tanto la clase "Zguillez" como la clase "Zah" implementen la Interface "IModerador".

Código :
public class Zguillez implements IModerador

Código :
public class Zah implements IModerador


De manera que ahora podemos asociar cualquiera de las dos clases a la propiedad "moderador".

Código :
var moderador:IModerador = new Zguillez();
// o var moderador:IModerador = new Zah();


Gracias al polimorfismo hemos ganado en dinamismo y reutilizabilidad, ya que podemos hacer que nuestra aplicación la "modere" Zguillez o Zah según nos interese.

Pero en este punto aun no nos hemos liberado de la dependencia de clases, ya que la clase que realiza la delegación está escrita en la implementación de la clase. Con lo que si quisiésemos cambiar la clase en la que delegamos tendríamos que reescribir la clase para colocar la otra.

Inyección de dependencias


Esto lo solucionamos con lo que se llama "Inyección de dependencias", que consiste en insertar los objetos a la clase en lugar de que sea la clase la que cree el objeto.

Haremos que la clase "Foro" no genere directamente el objeto que realizará la delegación sino que ese objeto le venga inyectado.

Código :
package 
{
   public class Foro
   {
      private var _moderador:IModerador;

      public function Foro()
      {
      }
      public function set moderador(m:IModerador):void
      {
         _moderador = m;
      }
      public function moderar():void
      {
         _moderador.moderar();
      }
   }
}


Para inyectarle la dependencia utilizaremos el setter.

Código :
var foro:Foro = new Foro();
var moderador:IModerador = new Zguillez();
foro.moderador = moderador;
foro.moderar();


De esta manera hemos conseguido que la clase "Foro" sea totalmente independiente de las clases que vayan a realizar la delegación haciendo el sistema totalmente reutilizable sin tener que realizar cambios en las clases existentes.