unit-testing - diseño por contrato java
¿Por qué el diseño por contrato no es tan popular en comparación con el desarrollo basado en pruebas? (9)
Creo que es mejor usar ambos métodos en conjunción en lugar de solo uno u otro.
Siempre me ha parecido que aplicar totalmente un contrato dentro de la clase y sus propios métodos puede ser poco práctico.
Por ejemplo, si una función dice que va a hacer un hash de una cadena por algún método y devolver la cadena hash como salida, ¿cómo hace la función para forzar que la cadena se haya corregido correctamente? Hash de nuevo y ver si coinciden? Parece tonto. Invierta el hash para ver si obtiene el original. Imposible. Por el contrario, necesita un conjunto de casos de prueba para garantizar que la función se comporta correctamente.
Por otro lado, si su implementación particular requiere que sus datos de entrada sean de un cierto tamaño, entonces establecer un contrato y aplicarlo en su código parece ser el mejor enfoque.
Puede pensar que esta pregunta es como esta pregunta hecha en StackOverflow anteriormente. Pero estoy tratando de ver las cosas de manera diferente.
En TDD, escribimos pruebas que incluyen diferentes condiciones, criterios, código de verificación. Si una clase pasa todas estas pruebas, estamos listos para continuar. Es una forma de asegurarse de que la clase realmente haga lo que se supone que debe hacer y nada más.
Si sigues palabra por palabra el libro de Bertrand Meyers '' Object Oriented Software Construction'' , la clase en sí tiene contratos internos y externos, por lo que solo hace lo que se supone que debe hacer y nada más. No se requieren pruebas externas porque el código para garantizar el cumplimiento del contrato es la parte de la clase.
Ejemplo rápido para aclarar las cosas
TDD
Cree una prueba para asegurarse de que en todos los casos un valor sea de (0-100)
Crea una clase que contenga un método que pase la prueba.
DBC
- Cree una clase, cree un contrato para ese miembro
var
a un rango de (0-100), establezca el contrato por incumplimiento del contrato, defina un método.
Personalmente me gusta el enfoque DBC.
¿Hay alguna razón por la cual el DBC puro no sea tan popular? ¿Son los idiomas o herramientas o Agile o solo soy yo a quien le gusta tener un código responsable de sí mismo?
Si crees que no estoy pensando bien, estaría más que dispuesto a aprender.
El principal problema con DBC es que en la gran mayoría de los casos, o bien el contrato no se puede especificar formalmente (al menos no convenientemente), o no se puede verificar con la herramienta de análisis estático actual. Hasta que superemos este punto para los principales idiomas (no Eiffel), DBC no dará el tipo de seguridad que la gente necesita.
En TDD, las pruebas son escritas por un ser humano en base a las especificaciones actuales de texto natural del método que (afortunadamente) están bien documentadas. Por lo tanto, un ser humano interpreta la corrección escribiendo la prueba y obtiene cierta seguridad basada en esa interpretación.
Si lees la guía de Sun para escribir JavaDocs, dice que la documentación debe esencialmente establecer un contrato suficiente para escribir un plan de prueba. Por lo tanto, el diseño por contrato no necesariamente se excluye mutuamente con TDD.
En mi opinión, TDD es más "inductivo". Comienza con ejemplos (casos de prueba) y su código incorpora la solución general a esos ejemplos.
DBC parece más "deductivo", después de reunir los requisitos usted determina el comportamiento del objeto y los contratos. A continuación, codifica la implementación específica de esos contratos.
La escritura de contratos es algo difícil, más que las pruebas que son ejemplos concretos de comportamiento, esto puede ser parte de la razón por la que TDD es más popular que DBC.
No veo ninguna razón por la cual ambos no puedan coexistir. Es maravilloso mirar un método y saber exactamente qué es el contrato. También es maravilloso saber que puedo ejecutar mis pruebas unitarias y saber que no se rompió nada con mi último cambio. Las dos técnicas no son mutuamente excluyentes. Por qué el diseño por contrato no es más popular es un misterio.
TDD y DbC son dos estrategias diferentes. DbC permite el fail-fast en el tiempo de ejecución mientras que el TDD actúa "en el momento de la compilación " (para ser exactos, agrega un nuevo paso justo después de la compilación para ejecutar las pruebas de la unidad).
Esa es una gran ventaja de TDD sobre DbC: permite obtener comentarios anteriores. Cuando escribe código de la manera TDD, obtiene el código y sus pruebas de unidad al mismo tiempo, puede verificar que "funciona" de acuerdo con lo que pensó que debería, que codificó en la prueba. Con DbC, obtienes código con pruebas integradas, pero aún tienes que ejercitarlo. OMI, esta ciertamente es una de las razones por las que Dbc no es tan popular.
Otras ventajas: TDD crea un conjunto de pruebas automáticas que permiten la detección (lectura de prevención) de regresiones y hace que la refabricación sea segura, para que pueda hacer crecer su diseño de forma incremental . DbC no ofrece esta posibilidad.
Ahora, usar DbC para fallar rápido puede ser muy útil, especialmente cuando su código interconecta otros componentes o tiene que depender de fuentes externas, en cuyo caso probar el contrato puede ahorrarle horas.
He usado ambos en el pasado y he encontrado que el estilo DBC es menos "intrusivo" . El controlador para DBC puede ser una aplicación normal ejecutándose. Para las Pruebas unitarias, debe encargarse de la configuración porque espera (valida) algunas respuestas. Para DBC no es necesario. Las reglas están escritas de manera independiente de los datos , por lo que no es necesario configurar y burlarse.
Más sobre mis experiencias con DBC / Python: http://blog.aplikacja.info/2012/04/classic-testing-vs-design-by-contract/
Veo Design By Contract como una especificación de éxito / fracaso en TODOS los casos, mientras que Test Driven Development se dirige a UN caso específico. Si el caso TDD tiene éxito, se supone que una función está haciendo su trabajo, pero no tiene en cuenta otros casos que podrían causar que falle.
El diseño por contrato, por otro lado, no garantiza necesariamente la respuesta deseada, solo que la respuesta es "correcta". Por ejemplo, si se supone que una función devuelve una cadena no nula, lo único que puede asumir en la ENSURE es que no será nula.
Pero tal vez no devuelve la cadena que se esperaba. No hay forma de que un contrato pueda determinar eso, solo una Prueba puede mostrar que estaba funcionando de acuerdo con la especificación.
Entonces los dos son complementarios.
Greg
El libro de Meyer no dice que nunca cometas errores. De hecho, si nos fijamos en el próximo capítulo, "Cuando el contrato está roto", se analiza lo que sucede cuando una función no logra lo que establece su contrato.
El simple hecho de que uses la técnica DbC no hace que tu código sea correcto. El diseño por contrato establece reglas bien definidas para su código y sus usuarios, en forma de contratos. Es útil, pero siempre puedes estropear las cosas de todos modos, solo que probablemente lo notarás antes.
El desarrollo basado en pruebas verificará, desde el mundo exterior, el estilo de recuadro negro, que la interfaz pública de su clase se comporta correctamente.
En conclusión, el desarrollo de diseño por contrato y basado en pruebas no son mutuamente excluyentes .
En primer lugar, soy un ingeniero de software de Eiffel, por lo que puedo hablar sobre el asunto desde la experiencia .
La premisa de TDD vs DbC es incorrecta
Las dos tecnologías no están en desacuerdo entre sí, sino que son complementarias entre sí. El complemento tiene que ver con la ubicación de las afirmaciones y el propósito.
El propósito de TDD tiene componentes y alcance. Los componentes básicos de TDD son las afirmaciones booleanas y la ejecución de la característica del objeto (por ejemplo, método). Los pasos son simples:
- Crea un objeto
- Ejecuta un código en una característica.
- Haga afirmaciones sobre el estado de los datos en el objeto.
Las afirmaciones que fallan, fallan la prueba. Pasar todas las aserciones es el objetivo.
Al igual que TDD, los contratos de Design-by-Contract tienen un propósito, alcance y componentes. Mientras que TDD se limita al tiempo de prueba de la unidad, los contratos pueden vivir a través de todo el SDLC (ciclo de vida de desarrollo de software). Dentro del alcance de TDD, la ejecución de métodos de objeto (características) ejecutará los contratos. En una configuración de prueba automática de Eiffel Studio (TDD), uno crea un objeto, realiza la llamada (al igual que TDD en otros idiomas), pero aquí es donde termina la similitud.
En Eiffel Studio con Auto-test y el código Eiffel con contratos, el propósito cambia algo. Queremos probar la relación Cliente-Proveedor. Nuestro código TDD pretende ser un cliente de nuestro método de proveedor en su objeto. Creamos nuestros objetos y llamamos a los métodos basados en este propósito, y no solo la simplista "prueba de método TDD-ish". Debido a que las llamadas a nuestros métodos (funciones) tienen contratos, esos contratos se ejecutarán como parte de nuestro código TDD-ish en Autoprueba. Como esto es cierto, las afirmaciones (pruebas) contractuales que colocamos en nuestro código NO tienen que aparecer en nuestro código de prueba TDD. Nuestro trabajo (como programador) es simplemente asegurarnos de que: A) El código + contratos se ejecuten a lo largo de todos los N-paths, y B) El código + contratos se ejecuten usando todos los tipos de datos y rangos razonables.
Quizás haya más para escribir sobre la relación de complemento TDD-DbC, pero no seré grosero con usted sobre el asunto. Baste decir que TDD y DbC NO están en desacuerdo con los demás, ¡ni mucho menos!
El poder de los contratos de DbC más allá de donde TDD puede alcanzar
¡Ahora podemos dirigir nuestra atención al poder de los contratos de Diseño por contrato más allá de donde TDD puede llegar!
Los contratos viven en el código. No son externos a él, sino internos. El bit más poderoso (más allá de la relación de contrato de cliente-proveedor) sobre los contratos es que el compilador está diseñado para conocerlos. ¡NO son una adición de perno a Eiffel! Por lo tanto, participan en todos los aspectos de la herencia (tanto la herencia vertical tradicional es una herencia como los genéricos laterales u horizontales). Además, llegan a un lugar al que TDD no puede acceder, dentro del método (función).
Si bien TDD puede simular condiciones previas y posteriores con cierta facilidad, TDD no puede acceder al código y realizar contratos invariables por bucle, ni puede realizar contratos periódicos de comprobación de puntos a lo largo de un bloque de código mientras se está ejecutando. Este es un poderoso paradigma lógico y cualitativo, y una realidad sobre cómo funciona el diseño por contrato.
Además, TDD no puede hacer invariantes de clase sino de la manera más leve posible. Hice todo lo posible para obtener mi código de prueba automática (que en realidad es solo la versión Eiffel Studios de TDD aplicada) para hacer el mimetismo invariante de clase. No es posible. Para entender por qué tendrías que saber lo in-and-out de cómo funcionan los invariantes de la clase Eiffel. Por lo tanto, por el momento, simplemente tendrá que asumir mi palabra (o no) de que TDD es incapaz de esta tarea, que DbC maneja tan fácil, bien y elegantemente.
El alcance de DbC no termina con las nociones anteriores
Señalamos anteriormente que TDD vive en el tiempo de prueba de la unidad. Los contratos, porque se aplican en código bajo la supervisión y el control del compilador, se aplican donde sea que se pueda ejecutar el código:
Banco de trabajo: usted, como programador, está utilizando el código para verlo funcionar (por ejemplo, antes de la hora TDD o en conjunción con la hora TDD).
Prueba de unidad: su prueba de integración continua, prueba de unidad, TDD, etc.
Prueba alfa: los usuarios de la prueba inicial tropezarán con los contratos mientras ejecutan el ejecutable
Beta-test: una audiencia más amplia de usuarios también tropezará con los contratos.
Producción: el ejecutable final (o sistema de producción) puede tener pruebas continuas aplicadas a través de contratos (TDD no puede).
En cada una de las situaciones anteriores, uno encontrará que uno tiene control sobre qué contratos se ejecutan y de qué fuentes. ¡Puede activar y desactivar varias formas de contrato y controlar con precisión extrema dónde y cuándo son aplicadas por el compilador!
Y si todo esto no fuera suficiente, los contratos (por diseño) pueden hacer algo que ninguna aseveración TDD puede hacer: decirle dónde está la pila de llamadas y qué relación cliente-proveedor está rota, y por qué ( lo que también sugiere inmediatamente cómo arreglarlo). ¿Por qué es esto cierto?
Las afirmaciones de TDD están diseñadas para informarle sobre los resultados de un código ejecutado (ejecución) después del hecho. La afirmación TDD solo puede ver hasta el estado actual del método bajo examen. Lo que las afirmaciones de TDD no pueden hacer desde su posición en el exterior de la base de código es decirle exactamente qué llamada falló y por qué. Verá, su llamada TDD inicial a algún método activará ese método. Muchas veces, ese método llamará a otro, y a otro, y a otro; a veces, a medida que la pila de llamadas sube y baja y acá y allá, hay una ruptura: algo escribe datos incorrectos, no los escribe ni escribe. cuando no debería.
TDD es como la policía que aparece en la escena del crimen después de que el asesinato ya ha sucedido. Todo lo que les queda son pistas forenses que esperan que los lleven a un sospechoso y una convicción. Pero, ¿y si pudiéramos estar allí ya que el crimen estaba teniendo lugar? Esa es la diferencia entre la ubicación de las aserciones TDD y las aserciones contractuales. ¡Los contratos están ahí para detectar el delito en curso y apuntan directamente al delincuente mientras comete la ofensa!
Resumen
Recapitulemos.
TDD no está en desacuerdo con DbC.
Es un complemento y un conjunto cooperativo de tecnologías, pero con diferentes funciones y propósitos, así como herramientas para trabajar con ellos.
Contrato llegar más lejos y revelar más acerca de su código cuando se rompe.
TDD es una forma de catalizador para la ejecución de contratos.
Al final del día: ¡quiero los dos! Después de leer todo esto (si sobrevivió), espero que lo haga también.