¿Cuál es la diferencia (si la hay) entre la ejecución del contrato y las pruebas unitarias? ¿Cuál es el equilibrio adecuado entre ellos?

La prueba y validación de la unidad (cumplimiento del contrato por parte del llamado) son cosas muy diferentes y complementarias. Ambos juntos dan como resultado un código MUCHO más confiable, con MUCHO menos tiempo dedicado a la depuración, de lo que uno podría ganar confiando solo en las pruebas unitarias o en la validación.

Para mostrar por qué las pruebas unitarias por sí solas no son suficientes, supongamos que tenemos el siguiente método:

public int methodM (int m)

donde m puede estar válidamente en el rango [0, 100]. Supongamos que methodM es llamado por methodN:

public int methodN (int n)

donde n puede estar válidamente en el rango [100, 200].

Suponga que el método N ha sido probado por la unidad para los siguientes valores de n: 100, 150, 200, es decir, un caso promedio típico (150) y dos casos límite válidos (100, 200). Funciona bien en todos esos casos. Sin embargo, suponga también que cuando n está entre 110 y 120, methodN llama a methodM con valores negativos de m, que no son válidos. Este error en el método N PODRÍA ser detectado a través de más pruebas unitarias del método N. Sin embargo, un valor de prueba elegido al azar de n, dentro del rango oficialmente válido del métodoN [100, 200], tiene solo una probabilidad de 1 en 10 de activar el error. Luego, una vez que se solucione el error, necesitaríamos un total de 101 pruebas unitarias del método N, una prueba para cada valor posible de n, para estar absolutamente seguros de que el método N nunca hace nada malo.

De todos modos, supongamos que todavía no hemos activado el error, y ahora escribimos otro método que llama a methodN:

public int methodP (int p)

donde p puede estar válidamente en el rango [200, 300]. Ahora hay al menos 2 tipos de errores que methodP puede tener con respecto a sus llamadas a methodN: (1) puede llamar a methodN usando valores no válidos de n, o (2) puede llamar a methodN usando valores de n que son oficialmente válidos pero qué método de activación M se llamará con valores no válidos de m. De cualquier manera, las posibilidades de activar el error con solo tres pruebas unitarias del método P son muy pequeñas.

Supongamos ahora que escribimos otro método que llama a methodP:

public int methodQ (int q)

Ahora hay al menos 3 tipos de errores que methodQ puede tener con respecto a sus llamadas al método P: (1) puede llamar al método P usando valores no válidos de p, (2) puede llamar al método P usando valores de p que son oficialmente válidos pero qué método de activación N se llamará con valores no válidos de n. o (3) puede llamar a methodN usando valores de n que son oficialmente válidos pero que activan a methodM para que se llame con valores no válidos de m. En todos los casos, es probable que las posibilidades de desencadenar el método Q pero con solo tres unidades de prueba sean muy pequeñas, por lo que es muy probable que el error termine en el código de producción.

Supongamos que el error finalmente se activa en la producción. Probablemente llevará horas rastrear este error.

En el ejemplo anterior, cada método por encima del método de nivel inferior M llama a otro método, por lo que el número de posibles fuentes de errores en el método de nivel superior Q aumenta linealmente con el número de métodos.

En realidad, la mayoría de los métodos de alto nivel llaman a más de un método de nivel inferior, por lo que el número de posibles fuentes de errores de nivel inferior en el método de nivel superior aumenta EXPONENCIALMENTE con el número de métodos. Un sistema con solo, digamos, 100 métodos puede tener fácilmente millones de diferentes rutas lógicas posibles a través del código. Obviamente, cubrir todas y cada una de ellas con pruebas unitarias no es factible.

Por lo tanto, en un sistema grande, si no realiza ninguna validación interna, puede llevar muchos días, semanas, meses o incluso años localizar un error, INCLUSO con pruebas unitarias razonablemente buenas.

Por otro lado, si todos los métodos públicos validan sus parámetros, entonces la gran mayoría de los errores se pueden rastrear INSTANTÁNEAMENTE sin importar cuán grande sea el sistema, e incluso aquellos errores que no se pueden encontrar instantáneamente se pueden aislar mucho más rápido de lo que posiblemente podría ser de otra manera.

Fuera de línea, me mencionó que ha tenido algo de mala suerte al intentar hacer cumplir el contrato en C. Me interesaría saber más sobre esto.

Sin embargo, parece que el mecanismo de excepción de Java debe ser mucho más limpio y confiable que cualquier cosa que esté utilizando para la ejecución de contratos en C. Nunca he tenido NINGUNA mala experiencia con el uso de excepciones de Java para validar parámetros. En mi experiencia, son un ENORME ahorro de tiempo sin consecuencias negativas, excepto en situaciones donde el alto rendimiento es crítico.

Como OP, tiendo a lo que dice Jeff Nelson, pero estoy de acuerdo con la propuesta que hizo mi co-desarrollador Dee Nixon. Originalmente publiqué la pregunta como una forma de probar un punto, pero no estoy completamente seguro de cuál es el equilibrio correcto. Era del campo que si haces el ciclo test-code-test- … -code con suficiente granularidad y comprobando suficientes casos extraños para que el tiempo de ejecución afectado por la ejecución del contrato sea prohibitivo. Dee preguntó por mis experiencias con esto en C:

foo (100); // bueno
foo (-100); // malo

foo (int bar)
{
si (barra <0)
error de fata;


}

eso solo fue un éxito de 5 veces en el rendimiento. Como mencioné a Dee fuera de línea, tanto el Servidor web Afterburner como el Error de corrección del protocolo de Internet ECIP probablemente habría tenido un gran impacto (lo suficiente como para que no cumplan los requisitos) si hubiéramos hecho esto. Esto podría ser más fiel a cosas como:

/ * escanea todas las ranuras y si está listo para leer o escribir hazlo * /
para (i = 0; i dpipe = 0;

/ * si una conexión válida lo hace
esperar una solicitud * /
if (conn [i] .fd> 0) {
conn [i] .stat = 1;
conn [i] .rtime = tiempo (NULL);
}

/ * no es necesario encontrar otra ranura * /
rotura;
}
/ * no intente leer o escribir este pase, puede causar
algunos estragos * /
Hacer continuación;
}

/ * escanea todas las ranuras y si está listo para leer o escribir hazlo * /
para (i = 0; i

Un programa moderno de diseño de pozos a) no tendría eso en main () yb) lo delegaría a una clase.

Supongo que su pregunta implica que la cobertura exhaustiva de las pruebas unitarias hace innecesaria la ejecución del contrato.

Estoy de acuerdo con eso, pero si y solo si todas las rutas y entradas de código posibles se prueban exhaustivamente, lo cual es difícil de garantizar. La mayoría de los IDE ni siquiera incluyen un complemento para medir la cobertura de pruebas unitarias.

More Interesting

¿Qué puede hacer un hombre indio casado de 33 años que trabaja como ingeniero de software para hacerse rico, de manera inteligente y honesta?

¿Por qué la GTU (Universidad Tecnológica de Gujarat) literalmente apesta?

¿Cuál es su experiencia con el alojamiento de Scrum más tarde o más temprano en el día para acomodar a los equipos de desarrollo de software Agile en diferentes zonas horarias?

¿Dónde puedo comprar softwares matemáticos?

¿Cuál es la diferencia entre la arquitectura MVC y N-Tiered?

¿Fue un error la programación orientada a objetos?

¿Cuáles son las opciones de carrera después de 3 años de experiencia en desarrollo de software en la industria de TI?

¿Qué compañía es una mejor opción considerando el crecimiento de la compañía, el crecimiento personal y profesional individual, el equilibrio entre la vida laboral y la compensación, como ingeniera de software femenina en Silicon Valley: Google o Facebook?

¿Puedes ganar más como desarrollador de software independiente que trabajar para una empresa?

¿Cuáles son los datos interesantes sobre Linux?

¿Por qué las páginas web enumeran varias fuentes (Ej: font-family: 'Helvetica', 'Times', 'Times New Roman', serif;) en lugar de una sola?

¿Cómo grabar o descargar desde LiveLeak? Hay un software que conozco, pero ¿cuál debo usar?

Consejo profesional: ¿Qué sería lo correcto para un ingeniero de software que era principalmente un desarrollador de C ++ para obtener una carrera de desarrollador web (desarrollador front-end)?

¿Cuál es la definición de profesionalismo para los ingenieros de software?

¿La mayoría de las compañías de software que usan tecnologías de Microsoft usan principalmente patrones de diseño ASP.net MVC?