Soy un programador algo defensivo y un gran admirador de los Contratos de Código de Microsofts.
Ahora no siempre puedo usar C # y en la mayoría de los lenguajes, la única herramienta que tengo son las aserciones. Por lo general, termino con un código como este:
class
{
function()
{
checkInvariants();
assert(/* requirement */);
try
{
/* implementation */
}
catch(...)
{
assert(/* exceptional ensures */);
}
finally
{
assert(/* ensures */);
checkInvariants();
}
}
void checkInvariants()
{
assert(/* invariant */);
}
}
Sin embargo, este paradigma (o como quiera llamarlo) conduce a una gran cantidad de desorden de código.
He comenzado a preguntarme si realmente vale la pena el esfuerzo y si la prueba de unidad adecuada ya cubriría esto.
testing
code-contracts
ronag
fuente
fuente
Respuestas:
No creo que debas pensar en ello como "vs".
Como se mencionó en los comentarios de @Giorgio, los contratos de código deben verificar invariantes (en el entorno de producción) y las pruebas unitarias deben asegurarse de que el código funcione como se espera cuando se cumplan esas condiciones.
fuente
Los contratos lo ayudan con al menos una cosa que las pruebas unitarias no hacen. Cuando está desarrollando una API pública, no puede hacer una prueba unitaria de cómo las personas usan su código. Sin embargo, aún puede definir contratos para sus métodos.
Yo personalmente sería tan riguroso con respecto a los contratos solo cuando se trata de la API pública de un módulo. En muchos otros casos, probablemente no valga la pena el esfuerzo (y puede usar pruebas unitarias en su lugar), pero esta es solo mi opinión.
Eso no significa que aconseje no pensar en contratos en esos casos. Siempre pienso en ellos. Simplemente no creo que sea necesario codificarlos siempre explícitamente.
fuente
Como ya se mencionó, los contratos y las pruebas unitarias tienen un propósito diferente.
Los contratos tienen que ver con la programación defensiva para asegurarse de que se cumplan los requisitos previos, el código se llama con los parámetros correctos, etc.
Pruebas unitarias para garantizar que el código funciona, en diferentes escenarios. Estos son como 'especificaciones con dientes'.
Las afirmaciones son buenas, hacen que el código sea robusto. Sin embargo, si le preocupa que esté agregando gran cantidad de código, también puede agregar puntos de interrupción condicionales en algunos lugares durante la depuración y reducir el recuento de afirmaciones.
fuente
Cualquier cosa que tenga en sus llamadas checkVariants () podría hacerse a partir de las pruebas, cuánto esfuerzo podría ser realmente depende de muchas cosas (dependencias externas, niveles de acoplamiento, etc.) pero limpiaría el código desde un punto de vista. Qué tan comprobable sería una base de código desarrollada contra aserciones sin alguna refactorización, no estoy seguro.
Estoy de acuerdo con @duros, estos no deben considerarse enfoques exclusivos o competitivos. De hecho, en un entorno TDD, incluso podría argumentar que las afirmaciones de 'requisito' serían necesarias para tener pruebas :).
Sin embargo, las afirmaciones NO hacen que el código sea más robusto a menos que realmente haga algo para corregir la verificación fallida, simplemente evitan que los datos se corrompan o sean similares, generalmente al abortar el procesamiento a la primera señal de problemas.
Por lo general, una solución probada / bien probada ya habrá pensado y / o descubierto muchas de las fuentes / razones de las entradas y salidas incorrectas mientras desarrollaba los componentes interactivos y ya los había abordado más cerca de la fuente del problema.
Si su fuente es externa y no tiene control sobre ella, entonces para evitar abarrotar su código que trata con otros problemas de códigos, puede considerar implementar algún tipo de componente de limpieza / aserción de datos entre la fuente y su componente y poner sus controles allí .
También tengo curiosidad por saber qué idiomas estás usando que no tienen algún tipo de xUnit u otra biblioteca de prueba que alguien haya desarrollado, ¿pensé que había algo para todo en estos días?
fuente
Además de las pruebas unitarias y los contratos de código, solo pensé en resaltar otro aspecto, que es el valor en la definición de sus interfaces para que elimine o reduzca la posibilidad de llamar a su código incorrectamente en primer lugar.
No siempre es fácil o posible, pero definitivamente vale la pena preguntarse: "¿Puedo hacer que este código sea más infalible?"
Anders Hejlsberg, creador de C #, dijo que uno de los mayores errores en C # no fue incluir tipos de referencia no anulables. Es una de las razones principales por las que existe el desorden de código de protección necesario.
Aún así, refactorizar para tener solo la cantidad necesaria y suficiente de código de protección hace, en mi experiencia, un código más utilizable y mantenible.
De acuerdo con @duros en el resto.
fuente
Haz ambas cosas, pero crea algunos métodos de ayuda estáticos para aclarar tus intenciones. Esto es lo que Google hizo para Java, consulte code.google.com/p/guava-libraries/wiki/PreconditionsExplained
fuente