unit test development unit-testing mocking

unit testing - test - ¿Mocks o clases reales?



spock unit test (11)

Está bien usar lo "real" siempre que tengas control absoluto sobre el objeto. Por ejemplo, si tiene un objeto que solo tiene propiedades y accesadores, probablemente esté bien. Si hay lógica en el objeto que desea usar, podría tener problemas.

Si una prueba unitaria para la clase a usa una instancia de clase b y un cambio introducido a b rompe b, entonces las pruebas para la clase a también se rompen. Aquí es donde puede tener problemas donde, como con un objeto simulado, siempre puede devolver el valor correcto. Usar "lo real" puede complicar las pruebas y ocultar el problema real.

Los simulacros también pueden tener inconvenientes, creo que hay un equilibrio con algunos simulacros y algunos objetos reales que tendrás que encontrar por ti mismo.

Esta pregunta ya tiene una respuesta aquí:

Las clases que utilizan otras clases (como miembros o como argumentos para los métodos) necesitan instancias que se comporten correctamente para la prueba unitaria. Si tiene estas clases disponibles y no introducen dependencias adicionales, ¿no es mejor utilizar la real en lugar de una simulación?


Si escribe su código en términos de interfaces, las pruebas unitarias se vuelven una alegría porque puede simplemente insertar una versión falsa de cualquier clase en la clase que está probando.

Por ejemplo, si su servidor de base de datos está inactivo por alguna razón, aún puede realizar pruebas unitarias escribiendo una clase de acceso a datos falsos que contenga algunos datos cocinados almacenados en la memoria en un mapa hash o algo así.


Si no le importa verificar las expectativas sobre cómo su UnitUnderTest debe interactuar con Thing, y las interacciones con RealThing no tienen otros efectos secundarios (o puede burlarse de ellos), en mi opinión, está perfectamente bien dejar que su UnitUnderTest usa RealThing.

Que la prueba cubra más de tu base de código es una bonificación.

Por lo general, me resulta fácil saber cuándo debo usar ThingMock en lugar de RealThing:

  • Cuando quiero verificar las expectativas en la interacción con la cosa.
  • Cuando se usa RealThing traerá efectos secundarios no deseados.
  • O cuando RealThing es simplemente demasiado difícil / problemático para usar en una configuración de prueba.

Si tus ''cosas reales'' son simplemente objetos de valor como JavaBeans, eso está bien.

Para algo más complejo, me preocuparía que a los simulacros generados a partir de marcos de burlas se les puedan dar expectativas precisas sobre cómo se usarán, por ejemplo, la cantidad de métodos llamados, la secuencia precisa y los parámetros esperados cada vez. Sus objetos reales no pueden hacer esto por usted, por lo que corre el riesgo de perder profundidad en sus pruebas.


Use el objeto real solo si primero se ha probado la unidad por sí mismo. Si introduce dependencias que lo impiden (dependencias circulares o si requiere que otras medidas estén en su lugar primero), utilice una clase ''simulada'' (generalmente denominada objeto "stub").


Yo digo que use clases reales cada vez que pueda.

Soy un gran creyente en ampliar los límites de las pruebas de "unidad" tanto como sea posible. En este punto, no son realmente pruebas unitarias en el sentido tradicional, sino más bien simplemente un conjunto de regresión automática para su aplicación. Todavía practico TDD y escribo todas mis pruebas primero, pero mis pruebas son un poco más grandes que las de la mayoría de las personas y mis ciclos verde-rojo-verde tardan un poco más. Pero ahora que he estado haciendo esto por un tiempo, estoy completamente convencido de que las pruebas unitarias en el sentido tradicional no son todo lo que se cree que son.

En mi experiencia, escribir un montón de pequeñas pruebas unitarias termina siendo un impedimento para la refactorización en el futuro. Si tengo una clase A que usa B y la unidad lo pruebo burlándose de B, cuando decido mover alguna funcionalidad de A a B o viceversa, todas mis pruebas y burlas tienen que cambiar. Ahora bien, si tengo pruebas que verifiquen que el flujo de extremo a extremo a través del sistema funciona como se espera, entonces mis pruebas realmente me ayudan a identificar los lugares donde mis refactorizaciones podrían haber causado un cambio en el comportamiento externo del sistema.

La conclusión es que los simulacros codifican el contrato de una clase en particular y a menudo terminan especificando algunos de los detalles de la implementación también. Si utiliza burlas extensamente a través de su conjunto de pruebas, su base de código termina con mucha inercia adicional que resistirá cualquier esfuerzo futuro de refactorización.


Extraído y extendido de una respuesta mía. ¿Cómo pruebo la unidad de los objetos que heredan? "> Aquí:

Siempre debe usar objetos reales siempre que sea posible.

Solo debe usar objetos simulados si los objetos reales hacen algo que no desea configurar (como usar sockets, puertos serie, obtener entradas del usuario, recuperar datos voluminosos, etc.). Básicamente, los objetos simulados son para cuando el esfuerzo estimado para implementar y mantener una prueba usando un objeto real es mayor que el de implementar y mantener una prueba usando un objeto simulado.

No creo en el argumento del "fracaso de la prueba dependiente". Si una prueba falla porque una clase dependiente se rompió, la prueba hizo exactamente lo que debería haber hecho. ¡Esto no es un olor! Si cambia una interfaz dependiente, ¡quiero saber!

Los entornos de prueba altamente burlados requieren un mantenimiento muy elevado, especialmente al principio de un proyecto cuando las interfaces están en constante cambio. Siempre me ha parecido mejor comenzar las pruebas de integración lo antes posible.


Hay una muy buena razón por la que desea usar stubs / mocks en lugar de clases reales. Es decir, para hacer que la prueba de su unidad (prueba de unidad pura) se pruebe aislada de todo lo demás. Esta propiedad es extremadamente útil y los beneficios para mantener las pruebas aisladas son abundantes:

  • Las pruebas se ejecutan más rápido porque no necesitan llamar a la implementación de clase real. Si la implementación se ejecuta en un sistema de archivos o una base de datos relacional, las pruebas se volverán lentas. Las pruebas lentas hacen que los desarrolladores no ejecuten pruebas unitarias con tanta frecuencia. Si está realizando un Test Driven Development, entonces las pruebas de tiempo acumulado son en conjunto una devastadora pérdida de tiempo para los desarrolladores.
  • Será más fácil rastrear problemas si la prueba está aislada de la clase bajo prueba. A diferencia de una prueba de sistema, será mucho más difícil rastrear errores desagradables que aparentemente no son visibles en los rastros de pila o lo que no.
  • Las pruebas son menos frágiles en los cambios realizados en las clases / interfaces externas porque estás simplemente probando la clase que está bajo prueba. La baja fragilidad es también una indicación de bajo acoplamiento, que es una buena ingeniería de software.
  • Está probando el comportamiento externo de una clase en lugar de la implementación interna que es más útil al decidir el diseño del código.

Ahora bien, si quiere usar una clase real en su examen, está bien, pero entonces NO es una prueba unitaria. En su lugar, está haciendo una prueba de integración, que es útil para validar los requisitos y comprobar la cordura en general . Las pruebas de integración no se ejecutan tan a menudo como las pruebas unitarias; en la práctica, se realiza principalmente antes de comprometerse con el repositorio de códigos favoritos, pero es igualmente importante.

Lo único que debes tener en cuenta es lo siguiente:

  • Los simulacros y los talones son para pruebas unitarias.

  • Las clases reales son para pruebas de integración / sistema.


He estado muy receloso de los objetos burlados desde que me han mordido varias veces. Son geniales cuando quieres pruebas de unidades aisladas, pero tienen un par de problemas. El problema principal es que si la clase Order necesita una colección de objetos OrderItem y se burla de ellos, es casi imposible verificar que el comportamiento de la clase OrderItem burlada coincide con el ejemplo real (duplicar los métodos con las firmas apropiadas generalmente no es suficiente). Más de una vez he visto que los sistemas fallan porque las clases falsas no coinciden con las reales y no hubo suficientes pruebas de integración para atrapar los casos límite.

Generalmente programo en lenguajes dinámicos y prefiero simplemente anular los métodos específicos que son problemáticos. Desafortunadamente, esto a veces es difícil de hacer en idiomas estáticos. La desventaja de este enfoque es que está utilizando pruebas de integración en lugar de pruebas unitarias y, a veces, es más difícil rastrear los errores. Lo bueno es que estás usando el código real que está escrito, en lugar de una versión burlada de ese código.


Depende de tu estilo de codificación , de lo que estás haciendo, de tu experiencia y de otras cosas.

Dado todo eso, no hay nada que te impida usar ambos .

Sé que uso el término prueba de unidad muy a menudo. Mucho de lo que hago podría llamarse mejor prueba de integración, pero mejor aún es solo considerarlo como prueba .

Entonces sugiero usar todas las técnicas de prueba donde encajan. El objetivo general es probar bien , tomar poco tiempo para hacerlo y tener una sensación sólida de que está bien .

Habiendo dicho eso, dependiendo de cómo programes, tal vez quieras considerar el uso de técnicas (como interfaces) que hagan que la burla sea menos intrusiva con más frecuencia. Pero no use interfaces e inyección donde sea incorrecto. Además, si el simulacro necesita ser bastante complejo, probablemente haya menos razones para usarlo. (Puede ver mucha buena orientación, en las respuestas aquí, a lo que le sirve cuando).

Dicho de otra manera: ninguna respuesta funciona siempre. Mantenga su ingenio sobre usted, observe lo que funciona lo que no funciona y por qué.


Siempre uso una versión simulada de una dependencia si la dependencia accede a un sistema externo como una base de datos o un servicio web.

Si ese no es el caso, entonces depende de la complejidad de los dos objetos. Probar el objeto bajo prueba con la dependencia real es esencialmente multiplicar los dos conjuntos de complejidades. Burlar la dependencia me permite aislar el objeto bajo prueba. Si cualquiera de los objetos es razonablemente simple, la complejidad combinada todavía es factible y no necesito una versión simulada.

Como han dicho otros, definir una interfaz en la dependencia e inyectarla en el objeto bajo prueba hace que sea mucho más fácil de simular.

Personalmente, no estoy seguro de si vale la pena utilizar simulaciones estrictas y validar todas las llamadas a la dependencia. Normalmente lo hago, pero es sobre todo un hábito.

También puede encontrar útiles estas preguntas relacionadas: