2008-10-07 11 views
11

Las clases que usan 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?¿Mocks o clases reales?

Respuesta

15

Digo que use clases reales cada vez que pueda.

Soy un gran creyente en la expansión de los límites de las pruebas de "unidades" 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 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.

+1

También me gusta ese enfoque, pero 1. Esa no es una prueba UNIT, pero parece una prueba de INTEGRACIÓN; 2. Me gusta, pero entiendo que este no es el mejor enfoque. – Budda

+3

"Me gusta, pero entiendo que este no es el mejor enfoque". Según quién? Hasta cierto punto, depende de cuáles sean tus objetivos. Si intenta escribir pruebas muy pequeñas que aíslan un método en particular para que pueda entender la mejor manera de implementar esa unidad, entonces los simulacros son útiles, pero si está intentando construir un conjunto de regresión que codificará el comportamiento de su sistema y alertarlo cuando cambia de manera inesperada, según mi experiencia, el uso extenso y falso definitivamente no es el mejor enfoque. –

+0

sí a "pruebas unitarias en el sentido tradicional no son todas las que están hechas polvo" – Kemoda

4

Está bien utilizar el "objeto real" siempre y cuando tenga 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 para b saltos b, 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 simuladores también pueden tener inconvenientes, creo que hay un equilibrio con algunos simulacros y algunos objetos reales que deberá encontrar por sí mismo.

3

Utilice el producto real solo si ha sido probado primero por la unidad. 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").

+0

¿No son las clases simuladas y los objetos de stub cosas diferentes? – Oddmund

+1

Puede haber un debate sobre sematics por ahí. Me gustan las distinciones de Fowler en su artículo: http://martinfowler.com/articles/mocksArentStubs.html Lo esencial es que los stubs simplemente sirven para un valor de retorno predeterminado, y que los simulacros son más para verificar que los objetos interactúen como se esperaba. . –

0

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

Para algo más complejo, me preocuparía que los simulacros generados a partir de marcos de burlas puedan tener expectativas precisas sobre cómo se usarán, p. 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.

0

Si escribe su código en términos de interfaces, entonces 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 algún motivo, puede realizar pruebas unitarias escribiendo una clase de acceso a datos falsos que contenga algunos datos almacenados almacenados en la memoria en un mapa hash o algo así.

1

Si no te importa verificar las expectativas sobre cómo tu UnitUnderTest debe interactuar con Thing, y las interacciones con RealThing no tienen otros efectos secundarios (o puedes burlarlos), en mi opinión, está perfectamente bien simplemente deje que su UnitUnderTest use RealThing.

Que la prueba luego cubra más de su código base es una ventaja.

por lo general parece que es fácil de decir cuando debería utilizar un ThingMock en lugar de un RealThing:

  • Cuando quiero para verificar las expectativas en la interacción con la cosa.
  • Al usar RealThing se producirían efectos secundarios no deseados.
  • O cuando RealThing es simplemente demasiado difícil/molesto de usar en una configuración de prueba.
4

Siempre uso una versión falsa de una dependencia si la dependencia accede a un sistema externo como una base de datos o 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 estas preguntas relacionadas útil:

1

he sido muy recelosos de burlado objetos ya 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.

4

Hay una muy buena razón por qué quiere usar stubs/mocks en lugar de clases reales. Es decir. para hacer que la prueba de su unidad (prueba de unidad pura) clase bajo prueba 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 la 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, 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á probando puramente 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 desea utilizar la clase real en la prueba, que está bien, pero entonces es NO una prueba de unidad. En su lugar, está realizando una prueba de integración, que es útil para los requisitos de validación y verificación de cordura 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 es necesario tener en cuenta es el siguiente:

  • burla de y talones son para las pruebas unitarias.

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

2

extrae y se extendió de una respuesta mía ¿Cómo puedo unidad de prueba de la herencia de objetos "> aquí:?

Siempre se debe utilizar objetos reales cuando sea posible

Sólo debe utilizar simulacro. objetos si los objetos reales hacen algo que no desea configurar (como usar sockets, puertos serie, obtener entrada del usuario, recuperar datos voluminosos, etc.). En esencia, los objetos simulados son para cuando el esfuerzo estimado para implementar y mantener una prueba usando un real objeto es mayor que eso para implementar y mantener una prueba utilizando un objeto simulado.

No considero el argumento "falla de 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 una interfaz dependiente cambia, ¡quiero saber!

entornos de prueba altamente burlaron son muy alto mantenimiento, sobre todo al principio de un proyecto cuando las interfaces están en proceso de cambio. Siempre me ha parecido mejor comenzar las pruebas de integración lo antes posible.

0

Depende de su estilo de codificación , lo que está haciendo, su experienciay otras cosas.

Teniendo en cuenta todo eso, no hay nada que lo de usando ambos.

Sé utilizar el término unidad de prueba demasiado a menudo. Mucho de lo que hago podría llamarse mejor prueba de integración, pero mejor aún es solo piense en ello como prueba.

Así que sugiero usar todas las técnicas de prueba donde quepan. El objetivo general es probar bien, tomar poco tiempo hacerlo y tener personalmente un sólida sensación de que es correcto.

Habiendo dicho eso, dependiendo de cómo programe, tal vez desee 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: Sin respuesta funciona siempre. Mantenga su ingenio sobre usted, observe lo que funciona lo que no funciona y por qué.