Soy bastante nuevo en el mundo de las pruebas unitarias, y esta semana decidí agregar cobertura de prueba para mi aplicación existente.
Esta es una tarea enorme, principalmente debido a la cantidad de clases para evaluar, pero también porque escribir exámenes es algo nuevo para mí.
Ya he escrito pruebas para un montón de clases, pero ahora me pregunto si lo estoy haciendo bien.
Cuando escribo pruebas para un método, tengo la sensación de reescribir por segunda vez lo que ya escribí en el método mismo.
Mis pruebas parecen estar tan ligadas al método (probar todas las rutas de código, esperando que algunos métodos internos se llamen varias veces, con ciertos argumentos), que parece que si alguna vez refactorizo el método, las pruebas fallarán incluso si el El comportamiento final del método no cambió.
Esto es solo un sentimiento, y como dije antes, no tengo experiencia en pruebas. Si algunos probadores más experimentados pudieran darme consejos sobre cómo escribir excelentes pruebas para una aplicación existente, eso sería muy apreciado.
Editar: Me gustaría agradecer a Stack Overflow, tuve excelentes aportes en menos de 15 minutos que respondieron más de las horas de lectura en línea que acabo de hacer.
fuente
Respuestas:
Creo que lo estás haciendo mal.
Una prueba unitaria debe:
No debe mirar dentro del método para ver lo que está haciendo, por lo que cambiar las partes internas no debería hacer que la prueba falle. No debe probar directamente que se están llamando métodos privados. Si está interesado en averiguar si su código privado se está probando, utilice una herramienta de cobertura de código. Pero no se obsesione con esto: el 100% de cobertura no es un requisito.
Si su método llama a métodos públicos en otras clases, y su interfaz garantiza estas llamadas, entonces puede probar que estas llamadas se realizan utilizando un marco de imitación.
No debe usar el método en sí (ni ninguno de los códigos internos que usa) para generar el resultado esperado dinámicamente. El resultado esperado debe estar codificado en su caso de prueba para que no cambie cuando cambie la implementación. Aquí hay un ejemplo simplificado de lo que debe hacer una prueba unitaria:
Tenga en cuenta que no se verifica cómo se calcula el resultado, solo que el resultado es correcto. Siga agregando más y más casos de prueba simples como los anteriores hasta que haya cubierto tantos escenarios como sea posible. Use su herramienta de cobertura de código para ver si se ha perdido alguna ruta interesante.
fuente
Para las pruebas unitarias, encontré que tanto Test Driven (pruebas primero, código segundo) como código primero, prueba segundo, son extremadamente útiles.
En lugar de escribir código, luego escribir prueba. Escribe el código y luego mira lo que PIENSAS que debería estar haciendo el código. Piense en todos los usos previstos y luego escriba una prueba para cada uno. Encuentro que las pruebas de escritura son más rápidas pero más complicadas que la codificación en sí. Las pruebas deben probar la intención. También pensando en las intenciones que terminas encontrando casos de esquina en la fase de escritura de prueba. Y, por supuesto, al escribir pruebas, puede encontrar que uno de los pocos usos causa un error (algo que a menudo encuentro, y estoy muy contento de que este error no haya corrompido los datos y no haya sido verificado).
Sin embargo, las pruebas son casi como codificar dos veces. De hecho, tuve aplicaciones donde había más código de prueba (cantidad) que código de aplicación. Un ejemplo fue una máquina de estado muy compleja. Tenía que asegurarme de que después de agregarle más lógica, todo funcionaba en todos los casos de uso anteriores. Y dado que esos casos eran bastante difíciles de seguir mirando el código, terminé teniendo un conjunto de pruebas tan bueno para esta máquina que estaba seguro de que no se rompería incluso después de hacer cambios, y las pruebas me salvaron el culo varias veces . Y a medida que los usuarios o probadores encontraban errores con los casos de flujo o de esquina no contabilizados, adivinen qué, se agregaron a las pruebas y nunca volvieron a suceder. Esto realmente les dio a los usuarios confianza en mi trabajo además de hacer que todo fuera súper estable. Y cuando tuvo que ser reescrito por razones de rendimiento, adivina qué,
Todos los ejemplos simples
function square(number)
son geniales y probablemente son malos candidatos para pasar mucho tiempo probando. Los que hacen lógica comercial importante, ahí es donde las pruebas son importantes. Prueba los requisitos. No solo pruebe las tuberías. Si los requisitos cambian, entonces adivine qué, las pruebas también deben hacerlo.Las pruebas no deberían ser literalmente pruebas de esa función para invocar la barra de funciones 3 veces. Eso está mal. Verifique si el resultado y los efectos secundarios son correctos, no la mecánica interna.
fuente
Vale la pena señalar que las pruebas unitarias de adaptación en el código existente son mucho más difíciles que impulsar la creación de ese código con pruebas en primer lugar. Esa es una de las grandes preguntas al tratar con aplicaciones heredadas ... ¿cómo hacer una prueba unitaria? Esto se ha preguntado muchas veces antes (por lo que puede ser cerrado como una pregunta tonta), y la gente generalmente termina aquí:
Mover el código existente al desarrollo impulsado por pruebas
Secundo la recomendación del libro de respuestas aceptadas, pero más allá de eso hay más información vinculada en las respuestas allí.
fuente
No escriba pruebas para obtener una cobertura completa de su código. Escriba pruebas que garanticen sus requisitos. Puede descubrir rutas de código innecesarias. Por el contrario, si son necesarios, están allí para cumplir algún tipo de requisito; búsquelo y pruebe el requisito (no la ruta).
Mantenga sus pruebas pequeñas: una prueba por requisito.
Más tarde, cuando necesite hacer un cambio (o escribir un nuevo código), intente escribir una prueba primero. Solo uno. Entonces habrá dado el primer paso en el desarrollo basado en pruebas.
fuente
Las pruebas unitarias se refieren al resultado que obtiene de una función / método / aplicación. No importa en absoluto cómo se produce el resultado, solo importa que sea correcto. Por lo tanto, su enfoque de contar llamadas a métodos internos y tal es incorrecto. Lo que tiendo a hacer es sentarme y escribir lo que debería devolver un método dados ciertos valores de entrada o un determinado entorno, luego escribir una prueba que compare el valor real devuelto con lo que se me ocurrió.
fuente
Intente escribir una Prueba de unidad antes de escribir el método que va a probar.
Eso definitivamente te obligará a pensar un poco diferente sobre cómo se están haciendo las cosas. No tendrá idea de cómo va a funcionar el método, solo lo que se supone que debe hacer.
Siempre debe probar los resultados del método, no cómo el método obtiene esos resultados.
fuente
Se supone que las pruebas mejoran la mantenibilidad. Si cambia un método y se rompe un examen, eso puede ser algo bueno. Por otro lado, si observa su método como un cuadro negro, no debería importar lo que haya dentro del método. El hecho es que necesita burlarse de las cosas para algunas pruebas, y en esos casos realmente no puede tratar el método como una caja negra. Lo único que puede hacer es escribir una prueba de integración: carga una instancia completamente instanciada del servicio que se está probando y hace que haga lo que se ejecuta en su aplicación. Entonces puedes tratarlo como una caja negra.
Esto se debe a que está escribiendo sus pruebas después de escribir su código. Si lo hiciera al revés (escribió las pruebas primero) no se sentiría de esta manera.
fuente