¿Deberíamos siempre unir los errores de prueba al corregirlos?

29

Cuando se corrigen errores, se recomienda que, donde trabajo, escriba primero una prueba que falla con el error dado y luego corrija el código hasta que pase la prueba. Esto sigue las prácticas de TDD, y se supone que es una buena práctica, pero noté que tiende a producir pruebas crípticas que se acercan mucho a la implementación.

Por ejemplo, tuvimos un problema cuando se envió un trabajo, alcanzó un cierto estado, se abortó y se volvió a intentar. Para reproducir este error, se escribió una prueba masiva con sincronización de subprocesos, muchas burlas y demás ... Hizo el trabajo, pero ahora que estoy refactorizando el código, me resulta muy tentador eliminar este mamut, ya que Realmente requeriría mucho trabajo (nuevamente) para adaptarse al nuevo diseño. Y solo está probando una pequeña característica en un solo caso específico.

De ahí mi pregunta: ¿cómo se prueban los errores que son difíciles de reproducir? ¿Cómo evita crear cosas que prueben la implementación y dañen la refactorización y la legibilidad?

Zonko
fuente
¿Es ese caso de error relevante para el nuevo diseño? Para mí, parte de un nuevo diseño sería aumentar la confiabilidad del diseño, por lo que podría usarlo para justificar la eliminación de este caso de prueba si este tipo de error tiene algo que ver con un error en el diseño original.
Thymine
el refactor se trata de otra cosa, y aún es posible en el nuevo diseño modificar ligeramente el código y reintroducir el error, que es lo que la prueba está observando. Supongo que una alternativa a la prueba sería un comentario en el código que diga "no jodas con esto", pero parece que es algo incorrecto: p
Zonko
1
si es demasiado complejo para una prueba de unidad, hágalo parte de la prueba de integración
Ratchet Freak
Parece que esto necesita una prueba de integración y no una prueba unitaria. Basado en el hecho de que te estás burlando tanto. Una regla general que he visto es que no se prueba el código que habla con componentes fuera de su base de código (hablar con una base de datos, leer desde el sistema de archivos, etc.) que suena como lo que está haciendo esto también.
Lucas

Respuestas:

27

Sí, en general deberías . Al igual que con todas las pautas, deberá usar su mejor criterio cuando se oponen a otras pautas. Para este escenario, la gravedad del error debe compararse con el trabajo necesario para implementar la prueba y la calidad de esa prueba al ser dirigida al problema comercial y detectar la regresión del estado del error.

Tendería a preferir escribir pruebas en lugar de no, ya que las interrupciones para eliminar errores tienden a tener más sobrecarga que simplemente desarrollar y mantener una prueba unitaria.

Telastyn
fuente
Agregaría más énfasis a esto y declararía que en un mundo ideal solo se consideraría un error si existe una prueba de unidad que falla, pero +1 para las cursivas y señalando que las necesidades comerciales deben prevalecer.
Joshua Drake
2
bueno, de hecho, al final se trata de un equilibrio entre el tiempo necesario para mantener la prueba y el tiempo que le tomaría a un novato detectar y corregir el error si volviera a ocurrir.
Zonko
También estaría a favor de la generalización de la prueba por lo que no está haciendo una prueba de abortar y volver a intentar el trabajo cuando llega a un estado específico, sino más bien las pruebas de abortar y volver a intentar en cada estado de un trabajo podría estar en.
Viejo Pro
+1 para "ya que las interrupciones para eliminar errores tienden a tener más sobrecarga que simplemente desarrollar y mantener una prueba unitaria".
Peter K.
16

Creo que la mejor práctica, una que me da vergüenza admitir que no sigo a menudo, es crear un sistema o prueba de integración que demuestre el problema observado en la producción, luego investigar para encontrar la (s) unidad (es) responsable (s) del problema, y luego escriba pruebas unitarias para aquellas unidades que demuestran el problema a nivel de unidad . Una vez que tenga las pruebas unitarias, repare las unidades y ejecute todas las pruebas. En este punto, puede ser prudente descartar la prueba original, porque puede ser frágil y / o lenta, pero mantenga las pruebas unitarias en su suite automatizada en aras de la regresión y la cobertura.

Carl Manaster
fuente
7

La práctica de escribir una prueba para identificar el defecto es una buena idea, ya que le permite identificar exactamente qué pasos son necesarios para reproducir el defecto y verificar que se haya solucionado. Además, estas pruebas se pueden ejecutar como parte de las pruebas de humo o de regresión para garantizar que los cambios posteriores no hayan reintroducido un defecto antiguo en el sistema.

Lo primero a considerar es el nivel de la prueba necesaria. Quizás la prueba para verificar la solución sería más apropiada a nivel de sistemas, o tal vez incluso una prueba de aceptación que se realiza manualmente. Creo que es más importante tener una prueba documentada y administrada, independientemente de cómo se implemente específicamente.

En cuanto a cómo la refactorización afecta las pruebas, depende de las características específicas. En algunos casos, la refactorización (o cualquier tipo de trabajo, como nuevas funciones) podría hacer que las pruebas ya no sean necesarias. El problema, como ocurrió originalmente, ya no es posible. En ese caso, puede ser conveniente eliminar la prueba de las posibles pruebas para que su proceso de prueba (automático o manual) sea más ágil. En otros casos, existen varios métodos para realizar la prueba y verificar la característica en un nivel diferente podría ser más apropiado. Si la característica es menor, quizás la prueba ya no sea necesaria.

También puede considerar no solo confiar en las pruebas, sino también iniciar sesión. Por ejemplo, capturar información en tiempo de ejecución (con diversos niveles de verbosidad según el entorno: más detallado durante las pruebas, menos detallado durante la implementación), perfilar la aplicación, capturar volcados del estado actual del sistema. Si puede encontrar desencadenantes comunes del problema, puede usarlo para guiar las pruebas en todos los niveles.

Thomas Owens
fuente
5

Si deberías.

Escribir pruebas unitarias para la base de código existente. Al corregir un error, debe asegurarse de que la prueba de su unidad falle; esto le dará la confianza de que realmente está trabajando en un error. Luego necesita re-factorizar y hacer pasar la prueba reparando el error.

Sin embargo, esta no es una práctica TDD. En las pruebas TDD, controle su diseño, pero en su caso, el diseño ya se ha decidido.

CodeART
fuente