¿Son muchas las aserciones que huelen el código?


19

Realmente me enamoré de las pruebas unitarias y TDD. Estoy infectado con las pruebas.

Sin embargo, las pruebas unitarias se usan normalmente para métodos públicos. A veces, aunque tengo que probar algunas suposiciones-aserciones en métodos privados también, porque algunas de ellas son "peligrosas" y la refactorización no puede ayudar más. (Lo sé, los marcos de prueba permiten probar métodos privados).

Entonces se convirtió en un hábito mío que la primera y la última línea de un método privado sean ambas afirmaciones.

Sin embargo, he notado que tiendo a usar aserciones en métodos públicos (así como en los privados) solo "para estar seguro". ¿Podría ser esto "prueba de duplicación" dado que los supuestos de métodos públicos son probados desde el exterior por el marco de prueba de la unidad?

¿Podría alguien pensar en demasiadas afirmaciones como un olor a código?


1
¿Estas afirmaciones están activadas o desactivadas en el lanzamiento?
jk.

Trabajo principalmente con Java, donde las aserciones tienen que habilitarse manualmente.
Florents Tselai

Acabo de encontrar este "Afirmaciones que aumentan la productividad rastreando errores" programmers.stackexchange.com/a/16317/32342
Florents Tselai

¿Cómo se define "demasiados"?
haylem

@haylem Hay "demasiadas" afirmaciones si otro programador lee el código y dice "Estoy cansado de estas afirmaciones".
Florents Tselai

Respuestas:


26

Esas afirmaciones son realmente útiles para probar sus suposiciones, pero también sirven para otro propósito realmente importante: la documentación. Cualquier lector de un método público puede leer las afirmaciones para determinar rápidamente las condiciones previas y posteriores, sin tener que mirar el conjunto de pruebas. Por esta razón, le recomiendo que guarde esas afirmaciones por razones de documentación, en lugar de razones de prueba. Técnicamente estás duplicando las afirmaciones, pero tienen dos propósitos diferentes y son muy útiles en ambos.

Mantenerlos como afirmaciones es mejor que simplemente usar comentarios, porque verifican activamente los supuestos cada vez que se ejecutan.


2
Muy buen punto!
Florents Tselai

1
Las afirmaciones son documentación, pero eso no hace que duplicarlas sea legítimo. Por el contrario, incluso genera sospechas sobre la calidad de las pruebas si parece que se necesitan las mismas afirmaciones más de una vez.
Yam Marcovic

1
@YamMarcovic Creo que esto es aceptable porque las dos piezas de código "duplicadas" tienen dos propósitos diferentes y distintos. Creo que es útil tenerlos en ambas ubicaciones, a pesar de la duplicación. Tener las afirmaciones en el método es invaluable para la documentación, y obviamente son necesarias para las pruebas.
Oleksi

2
Las afirmaciones en código para mí son complementarias a las afirmaciones de prueba de unidad. No tendrá una cobertura de prueba del 100% y las afirmaciones en el código lo ayudarán a reducir las pruebas unitarias fallidas más rápido.
sebastiangeiger

15

Parece que estás tratando de hacer Diseño por contrato a mano.

Hacer DbC es una buena idea, pero al menos debería considerar cambiar a un lenguaje que lo admita de forma nativa (como Eiffel ) o al menos usar un marco de contrato para su plataforma (por ejemplo, Microsoft Code Contracts para .NETes bastante agradable, posiblemente el marco de contrato más sofisticado que existe, incluso más poderoso que Eiffel). De esa manera, puede aprovechar mejor el poder de los contratos. Por ejemplo, utilizando un marco existente, puede mostrar los contratos en su documentación o un IDE (por ejemplo, los contratos de código para .NET se muestran en IntelliSense y, si tiene VS Ultimate, incluso puede comprobarse estáticamente por un probador de teoremas automático en tiempo de compilación mientras escribe; asimismo, muchos marcos de contratos Java tienen doclets JavaDoc que extraerán automáticamente los contratos en la documentación de su API JavaDoc).

E incluso si resulta que en su situación no hay alternativa a hacerlo manualmente, ahora al menos sabe cómo se llama y puede leer sobre ello.

En resumen: si realmente está haciendo DbC, incluso si no lo sabe, esas afirmaciones están perfectamente bien.


4

Siempre que no tenga control total sobre sus parámetros de entrada, es una muy buena idea probar por adelantado los errores simples. Falla en nulo por ejemplo.

Esta no es la duplicación de las pruebas, ya que debe probar que el código falla apropiada dada parámetros de entrada incorrecta, y luego documentar que .

No creo que deba afirmar los parámetros de retorno (a menos que explícitamente tenga una invariante que quiera que el lector entienda). Este es también el trabajo de las pruebas unitarias.

Personalmente, no me gusta la assertdeclaración en Java, ya que se pueden desactivar y es una falsa seguridad.


1
+1 Para "No me gusta la declaración de aserción en Java, ya que se pueden desactivar y es una falsa seguridad".
Florents Tselai

3

Creo que usar afirmaciones en métodos públicos es aún más importante, ya que allí no controlas las entradas y es más probable que se rompa una suposición.

Las condiciones de entrada de prueba deben realizarse en todos los métodos públicos y protegidos. Si las entradas se pasan directamente a un método privado, entonces probar sus entradas puede ser redundante.

Las condiciones de salida de prueba (por ejemplo, el estado del objeto o ese valor de retorno! = Nulo) deben realizarse en los métodos internos (por ejemplo, métodos privados). Si las salidas se pasan directamente de un método privado a la salida de un método público sin cálculos adicionales o cambios de estado interno, entonces probar las condiciones de salida del método público puede ser redundante.

Sin embargo, estoy de acuerdo con Oleksi en que la redundancia puede aumentar la legibilidad y también puede aumentar la capacidad de mantenimiento (si la asignación directa o la devolución ya no es el caso en el futuro).


44
Si el usuario (persona que llama) de la interfaz pública puede causar una condición de error o una condición de argumento no válida, se le debe dar el tratamiento completo adecuado para el manejo de errores. Esto significa formatear un mensaje de error que sea significativo para el usuario final, acompañado de un código de error, registro e intentos de recuperar datos o restaurar a un estado sano. Sustituir el manejo adecuado de errores con aserciones hará que el código sea menos robusto y más difícil de solucionar.
rwong

Las afirmaciones podrían (y en muchos casos deberían) incluir excepciones de registro y lanzamiento (o códigos de error en C--).
Danny Varod

3

Es difícil ser independiente del lenguaje sobre este problema, ya que los detalles de cómo se implementan las aserciones y el manejo 'apropiado' de errores / excepciones tienen relación con la respuesta. Aquí están mis $ 0.02, basados ​​en mi conocimiento de Java y C ++.

Las afirmaciones en métodos privados son una buena cosa, suponiendo que no se exceda y las ponga en todas partes . Si los está poniendo en métodos realmente simples o verificando repetidamente cosas como campos inmutables, entonces está abarrotando el código innecesariamente.

Por lo general, es mejor evitar las afirmaciones en métodos públicos. Todavía debe hacer cosas como verificar que el contrato del método no se viole, pero si lo es, entonces debería lanzar excepciones adecuadamente tipadas con mensajes significativos, revertir el estado donde sea posible, etc. (lo que @rwong llama "el total tratamiento adecuado de manejo de errores ").

En términos generales, solo debe usar aserciones para ayudar a su desarrollo / depuración. No puede asumir que quien use su API tendrá habilitadas las aserciones cuando use su código. Aunque tienen algún uso para ayudar a documentar el código, a menudo hay mejores formas de documentar las mismas cosas (es decir, documentación del método, excepciones).


2

Agregar a la lista de respuestas (en su mayoría) excepcionales, otra razón para muchas afirmaciones y duplicaciones, es que no sabes cómo, cuándo o por quién se modificará la clase durante su vida útil. Si está escribiendo un código desechable que estará muerto en un año, no es una preocupación. Si está escribiendo un código que estará en uso en más de 20 años, se verá bastante diferente, y una suposición que haya hecho puede que ya no sea válida. El tipo que hace ese cambio te agradecerá por las afirmaciones.

Además, no todos los programadores son perfectos; de nuevo, las afirmaciones significan que uno de esos "errores" no se propagará demasiado.

No subestime el efecto que estas afirmaciones (y otras verificaciones sobre los supuestos) tendrán en la reducción del costo de mantenimiento.


0

Tener afirmaciones duplicadas no está mal per se, pero tener las afirmaciones "solo para asegurarse" no es lo mejor. Realmente se reduce a lo que exactamente cada prueba está tratando de lograr. Solo afirme lo que es absolutamente necesario. Si una prueba usa Moq para verificar que se invoca un método, en realidad no importa lo que suceda después de invocar ese método, la prueba solo se preocupa por asegurarse de que se invoque el método.

Si cada prueba de unidad individual tiene el mismo conjunto de afirmaciones, excepto una o dos, entonces cuando refactoriza un poco, todas las pruebas pueden fallar exactamente por la misma razón, en lugar de mostrarte el verdadero impacto del refactor. Podrías terminar en una situación en la que ejecutas todas las pruebas, todas fallan porque cada prueba tiene las mismas afirmaciones, corriges esa falla, ejecutas las pruebas nuevamente, fallan por otra razón, corriges esa falla. Repita hasta que esté listo.

También considere el mantenimiento de su proyecto de prueba. ¿Qué sucede cuando agrega un nuevo parámetro, o la salida se modifica ligeramente, tendría que volver a pasar por un montón de pruebas y cambiar una afirmación o agregar una afirmación? Y, dependiendo de su código, podría terminar teniendo que configurar mucho más solo para asegurarse de que todas las afirmaciones pasen.

Puedo entender el atractivo de querer incluir afirmaciones duplicadas en la prueba unitaria, pero realmente es exagerado. Seguí el mismo camino con mi primer proyecto de prueba. Me alejé porque el mantenimiento solo me estaba volviendo loco.


0

Sin embargo, las pruebas unitarias se utilizan para métodos públicos.

Si su marco de prueba permite accesores, entonces la prueba unitaria de métodos privados también es una buena idea (IMO).

¿Podría alguien pensar en demasiadas afirmaciones como un olor a código?

Hago. Las afirmaciones están bien, pero su primer objetivo debe ser lograr que el escenario ni siquiera sea posible ; no solo que no suceda.


-2

Es una mala practica.

No debe probar métodos públicos / privados. Debes probar la clase como un todo. Solo asegúrese de tener una buena cobertura.

Si sigue la forma (primitiva) de probar cada método por separado como regla general, le resultará extremadamente difícil refactorizar su clase en el futuro. Y esa es una de las mejores cosas que TDD le permite hacer.

Además, es duplicación. Además, sugiere que el escritor del código realmente no sabía lo que estaba haciendo. Y lo curioso es que sí .

Algunas personas tratan sus pruebas con menos cuidado que su código de producción. No deberían. En todo caso, mi experiencia sugiere incluso tratar las pruebas con más cuidado. Valen la pena


-1 La prueba de la unidad en una clase en su conjunto puede ser peligrosa, ya que terminas probando más de una cosa por prueba. Esto hace pruebas realmente frágiles. Debería probar un comportamiento a la vez, que a menudo corresponde a probar solo un método por prueba.
Oleksi

También con respecto a "sugiere que el escritor del código realmente no sabía lo que estaba haciendo [...] usted". Los futuros desarrolladores podrían no ser tan claros sobre lo que hace un método en particular. En este caso, tener condiciones previas y posteriores en forma de afirmaciones puede ser invaluable para comprender un código.
Oleksi

2
@Oleksi Buenas pruebas se trata de escenarios de uso válidos, no de afirmar hasta el más mínimo detalle irrelevante. Las afirmaciones redundantes son de hecho un olor a código debido a la intención oscura, y son solo otro mal ejemplo de programación defensiva.
Yam Marcovic
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.