2009-12-15 11 views
31

Leí a través de TDD: Only mock types you own entrada de Mark Needham y me gustaría saber si esta es la mejor práctica o no?¿Debería solo simular los tipos que posee?

Tenga en cuenta que él no está en contra de la burla, sino en contra de la burla directa - él dice que escribir un envoltorio y burlarse de eso está bien.

+2

Este artículo me resulta muy útil para explicar este problema: http://davesquared.net/2011/04/dont-mock-types-you-dont-own.html – Dmitry

Respuesta

16

Depende si quiere decir simulacro o simulacro ™ & hellip;

Dado que solo está utilizando un marco simulado (como, por ejemplo, Mockito) para crear talones, la creación de talones de los tipos que no le pertenecen es totalmente correcto y razonable.

Sin embargo, si está utilizando un marco simulacro (como por ejemplo Mockito) para crear objetos simulados ™, entonces mejor sigue literalmente el consejo de los simulacros ™ evangelistas. Personalmente, perdí contacto con ese movimiento, por lo que no puedo decirte si el consejo de Mark Needham es considerarlo kosher o no.

Dejando de lado la ironía, lo que Mark escribe sobre la burla de EntityManagers en Hibernate suena razonable por sí mismo. Pero dudo que podamos generalizar una regla como "nunca se burle de tipos que no le pertenecen" a partir de ese caso específico. En algún momento podría tener sentido, a veces no.

7

Iba a decir "no", pero al echar un rápido vistazo a la publicación del blog puedo ver de qué se trata.

Habla específicamente sobre burlas de EntityManagers en Hibernate. Estoy en contra de esto Los administradores de entidades deben estar ocultos dentro de los DAO (o similares) y los DAO son lo que se debe burlar. Probar llamadas de una línea a EntityManager es una pérdida total de tiempo y se interrumpirá tan pronto como algo cambie.

Pero si tiene código de terceros que desea probar cómo interactúa con él, por supuesto.

0

Estoy de acuerdo con que Mark dice. Desafortunadamente no se puede burlar de todo y hay algunas cosas de las que no se quiere burlar, solo porque su uso normal es una caja negra.

Mi regla de oro es la simulación de las cosas que harán que la prueba sea rápida pero no hará que la prueba sea escamosa. Recuerde que no todas las falsificaciones son iguales y Mocks are not Stubs.

+0

En realidad, PUEDES burlarse de todo. Para mí, la diferencia real no es entre burlas y talones, sino entre expectativas estrictas y no estrictas. Y las expectativas asociadas a cualquier objeto simulado pueden ser estrictas o no. –

23

Mi respuesta es "no". Deberías burlarte de todo lo que tenga sentido en el contexto de una prueba unitaria determinada. No debería importar si eres "dueño" del tipo burlado o no.

En estos días, en un entorno Java o .NET todo (y realmente me refiero a todo) se puede burlar fácilmente. Por lo tanto, no hay ninguna razón técnica para tomarse la molestia de escribir primero el código de envoltura adicional.


Algunas ideas adicionales que he estado pensando recientemente (noviembre de 2010), que muestran cómo ilógico "únicos tipos de simulacros de su propiedad" puede ser:

  1. Supongamos que hace crear un contenedor para una API de terceros, y luego se burla del envoltorio en pruebas unitarias. Más tarde, sin embargo, imagina que el contenedor se puede reutilizar en otra aplicación, por lo que lo mueve a una biblioteca separada. Así que ahora el envoltorio ya no es "propiedad suya" (ya que se usa en múltiples aplicaciones, potencialmente mantenidas por diferentes equipos). ¿Los desarrolladores deberían crear un nuevo contenedor para el anterior?!? ¿Y seguir haciéndolo recursivamente, añadiendo capa tras capa de código esencialmente inútil?
  2. Supongamos que alguien más ya ha creado un buen contenedor para alguna API no trivial y lo ha puesto a disposición como una biblioteca reutilizable. Si dicho contenedor es justo lo que necesito para mi caso de uso específico, ¿debería crear primero un contenedor para el contenedor, con una API casi idéntica, para que yo sea el "propietario" de él?

Para un ejemplo concreto y realista, considere la Apache Commons Email API, que no es más que un envoltorio para la API Java Mail estándar. Como no soy el propietario, ¿siempre debería crear un contenedor para la API de correo electrónico de Commons, siempre que escribo pruebas de unidad para una clase que necesita enviar un correo electrónico?

+1

Lo siento, pero no sé por qué esto se sube más de https://stackoverflow.com/a/31938571/2711378 Esa respuesta lo explica bien. La regla tiene sentido. Una biblioteca que usted posee no significa definida en el mismo paquete. Usted sabe cuándo se cambia una implementación para una biblioteca que usted creó. Incluso si se trata de una biblioteca de código abierto y si sabe cuándo cambia, bueno, adelante y fingirlo. Pero eso se pone difícil cuando no sabes cuándo cambia la implementación. Demonios, a veces la api puede cambiar, por eso usamos envoltorios en primer lugar. –

1

Definitivamente soy de una minoría, pero considero que Mocking es un Code Olor y, en su lugar, uso la inyección de dependencia si es posible. El razonamiento es que burlarse es básicamente una solución para probar algunos códigos difíciles de probar. Las burlas debilitan las pruebas porque se comportan (en el mejor de los casos) como una versión específica de una biblioteca. Si la biblioteca cambia, su prueba perderá todo su valor de comprobación.

Se puede ver de leer lo anterior que estoy usando los propios argumentos de Mark Needham, pero no por decir que usted no debe simulacro objeto de no poseer, pero que no debe burlarse de todo ...

OK, si la inyección de dependencia no es una opción, entonces vamos a burlarnos ... pero luego debes entender que tu prueba es falsa y no se comportará como un código de producción. Esa no es una prueba de unidad real, solo una parcialmente falsificada. Si es posible, puede hacerlo menos añadiendo pruebas que comprueben que el comportamiento es el esperado para los Objetos burlados.

+6

Creo que tal vez su definición de "inyección de dependencia" o "burla" es diferente a la habitual. No se excluyen mutuamente, sino que son complementarios: a veces se inyecta la implementación real de una dependencia (producción, prueba de integración) y, a veces, se inyectan burlas (pruebas unitarias). – Ladlestein

+0

@Ladlestein: No lo creo, pero tal vez. Uso "Mocking" cuando estoy cambiando el comportamiento de algo que no es un parámetro explícito (generalmente una biblioteca global). Hablo de "Dependency Injection" cuando estoy refactorizando el código para reemplazar un parámetro implícito con un objeto explícito proporcionado como parámetro (ya sea a través del constructor de objetos probado o de algún método). Y, de hecho, al proporcionar algún objeto de prueba ya no hablo de Mock, sino de Stubs (si no es una implementación real) o de Fakes (implementación real pero simplificada para propósitos de prueba). – kriss

13

Me gusta el explanation the Mockito project gives a esta pregunta.

¡No se burle del tipo que no es suyo!

Esto no es una línea dura, pero cruzar esta línea puede tener repercusiones ! (lo más probable es que lo haga)

  1. Imagine el código que se burla de una lib de terceros. Después de una actualización particular de una tercera biblioteca, la lógica puede cambiar un poco, pero la suite de prueba se ejecutará bien, porque se burla. Así que adelante, pensando que todo es bueno para ir, la acumulación de la pared es de color verde después de todo, el software se implementa y ... Boom
  2. Puede ser una señal de que el diseño actual no está desacoplada lo suficiente de esta tercera biblioteca de fiestas
  3. También otro problema es que la lib de terceros puede ser compleja y requerir muchos simulacros para funcionar correctamente. Eso lleva a pruebas excesivamente especificadas y accesorios complejos, lo que en sí mismo compromete el objetivo compacto y legible . O a pruebas que no cubren el código suficiente, debido a la complejidad para burlarse del sistema externo.

En cambio, la forma más común es la creación de envolturas alrededor del lib/sistema externo, aunque uno debe ser consciente del riesgo de abstracción fugas, donde demasiada API de bajo nivel, conceptos o excepciones, va más allá del límite de la envoltura. Para verificar la integración con la biblioteca de terceros, escriba las pruebas de integración y haga que sean lo más compactos y legibles posible.

+0

¿Qué significa: "fuga de abstracción, donde demasiado API de bajo nivel, conceptos o excepciones, va más allá del límite del contenedor"? – ieXcept

+1

Las pruebas unitarias son sobre aislamiento. No creo que la burla sea un problema, siempre y cuando tengas responsabilidad en tus pruebas de integración. Envolver por el bien de las pruebas puede presentar sus propias complejidades: a menos que sean triviales, en algún momento sus envolturas también tendrán que ser probadas. – NBJack

+0

Por lo que vale, creo que esta debería ser la respuesta aceptada. –

0

En mi humilde opinión, la cuestión de la propiedad es irrelevante.

La pregunta relevante es la de acoplamiento, es decir, qué especifica el código de prueba. Ciertamente no quiere un código de prueba que especifique los detalles de la API de alguna biblioteca que utilice. Esto es lo que obtienes cuando, por ejemplo, usa Mockito para burlarse de la biblioteca directamente en tu clase de prueba.

A widespread solution proposal para este problema es crear un envoltorio alrededor de la biblioteca y luego burlarse del envoltorio. Pero esto tiene los siguientes inconvenientes:

  • El código dentro del envoltorio no está probado.
  • El contenedor puede ser una abstracción imperfecta, por lo que puede ser necesario cambiar la API del contenedor. Si se burló de la envoltura en muchas pruebas, debe adaptar todas estas pruebas.

Así que en vez, recomendaría a pruebas desacoplarse de las interfaces de código productiva por completo. No coloque los simulacros en el código de prueba directamente, sino que cree una clase de stub separada que implemente o se burle de la interfaz productiva. A continuación, agregue una segunda interfaz al resguardo que permite que las pruebas realicen la configuración o las afirmaciones necesarias. Luego, solo necesita adaptar una clase en caso de que cambie la interfaz productiva, e incluso podría darse el lujo de burlarse de la interfaz de una biblioteca que es compleja o cambia con frecuencia.

Cuestiones relacionadas