He estado haciendo TDD durante un año, me siento bastante bien al respecto, me encantan mis suites de prueba y todo. Sin embargo, he notado que últimamente he estado haciendo muchas verificaciones de llamadas simuladas. Por ejemplo, tendría un Servicio en el que se inyectaría un Repositorio; en mi prueba unitaria, pasaría un simulacro del Repositorio y verificaría que fue invocado dentro del método que estoy probando. Luego verificaría si los resultados devueltos son correctos (en otra prueba). Esto definitivamente "se siente" mal, ya que las pruebas de mi unidad ahora están muy acopladas a los detalles de implementación. Escuché que deberías probar el "comportamiento", sin embargo, en muchas situaciones eso es ... emm, ¿no es posible? Si tienes unvoid
método, por ejemplo, generalmente prueba los efectos secundarios. Quiero decir que es fácil seguir adelante y mostrar algunos códigos kata simples donde esto se puede demostrar, pero en mi humilde opinión, no se refleja muy bien en los programas del mundo real que escribimos. ¿Lo que estoy haciendo está mal? ¿Es este tipo de prueba una especie de antipatrón? Agradecería su opinión sobre esto, todavía soy un poco novato cuando se trata de TDD.
fuente
Respuestas:
Bueno, deberías intentar probar entradas y salidas. Debería verificar el comportamiento visible desde el exterior. Las "promesas" o "contratos" que hace tu clase.
Al mismo tiempo, a veces no hay mejor manera de probar un método que hacer lo que dijiste.
Creo que hace que su prueba sea más frágil, por lo que debe evitar las pruebas que dependen de los detalles de implementación si puede, pero no es un trato de todo o nada. A veces está bien, lo peor que sucede es que cambias la implementación y tienes que actualizar la prueba.
fuente
El propósito de una prueba es restringir las posibles implementaciones productivas. Asegúrese de que solo impone restricciones a la implementación que realmente necesita. Por lo general, esto es lo que debe hacer su programa, y no cómo lo hace.
Entonces, si, por ejemplo, su servicio agrega algo al repositorio, debe probar que la nueva entrada está contenida en el repositorio después, y no que se active la acción de agregar.
Para que esto funcione, debe poder utilizar la implementación del repositorio (probado en otro lugar) en la prueba del servicio. Descubrí que usar la implementación real de un colaborador es generalmente un buen enfoque, porque es realmente la mejor implementación disponible.
"Entonces, ¿pero qué pasa si usar las implementaciones reales en la prueba es costoso (por ejemplo, porque requieren recursos que son complicados de configurar)? Necesito usar simulacros en este caso, ¿verdad?"
En cualquier caso, es probable que desee una prueba de integración que pruebe que las implementaciones reales funcionan juntas. Asegúrese de que esta prueba de integración sea todo lo que se necesita para probar su servicio. O en otras palabras: si un servicio reúne a muchos colaboradores (y, por lo tanto, es potencialmente difícil de probar), asegúrese de que no contenga ninguna lógica. Si lo hace, y necesitaría múltiples pruebas (de integración), debe cambiar la estructura de su código, por ejemplo, aislando la lógica y, por lo tanto, haciéndola más verificable.
El uso de simulacros en este caso alivia el dolor de probar una pieza de lógica mal aislada y, por lo tanto, oculta un problema arquitectónico . Por lo tanto, no use simulacros para probar código mal estructurado, sino corrija la estructura.
fuente
Mis pensamientos son: 'servicios agregados'.
La verificación de llamadas hará esto, pero no proporcionará mucho valor. Solo estás revisando tu cableado.
Hay 3 formas no exclusivas de esto:
Extienda las pruebas que tiene para cada servicio individual para que verifique el comportamiento de nivel superior. Por ejemplo, si está llegando a una base de datos en memoria en sus pruebas unitarias del servicio, aumente un nivel para que esté probando el servicio contra una base de datos real. La capa de servicio está más arriba en el árbol de abstracción, y su prueba también debería hacerlo.
Use la generación de código para crear el servicio directamente desde los servicios agregados.
Use algún tipo de reflexión o lenguaje dinámico para hacer lo mismo. Por ejemplo, en Java, puede ser posible utilizar una interfaz maravillosa, que pasa la llamada directamente.
Probablemente hay otras formas de hacer esto, pero solo verificar el cableado tiene un rendimiento muy bajo y lo conectará a esta implementación.
fuente