2009-07-10 13 views
12

utilizo NMock2, y he redactado las siguientes clases NMock para representar algunos conceptos marco simulacros comunes:¿Cuándo esperar y cuándo anular?

  • Expect: esto especifica qué método debe devolver burlado y dice que la llamada debe ocurrir o la prueba falla (cuando va acompañado de una llamada al VerifyAllExpectationsHaveBeenMet()).

  • Stub: esto especifica lo que debe devolver un método falso pero no puede hacer que falle una prueba.

¿Qué debo hacer cuando?

+0

Esperar devolverá nada mientras Stub devolverá lo que especifique. ambos 'resuelven' el método una vez – zinking

Respuesta

15

Una gran cantidad de burlarse de los marcos están trayendo los conceptos de burla & talones estrechos & más juntos hasta el punto de que pueden ser considerados funcionalmente casi lo mismo. Desde un punto de vista conceptual, sin embargo, por lo general tratan de seguir esta convención:

  • Mock: Sólo cuando se está tratando de forma explícita para verificar el comportamiento del objeto bajo prueba (es decir, la prueba está diciendo que este objeto debe llamar ese objeto).
  • Stub: Cuando intenta probar algunas funciones/comportamientos, pero para que funcionen debe confiar en algunos objetos externos (es decir, su prueba indica que este objeto debe hacer algo, pero como un lado efecto, puede llamar a ese objeto)

Esto se vuelve más claro cuando se asegura de que cada una de las pruebas de su unidad solo pruebe una cosa. Claro que si intentas probar todo en una prueba, entonces también podrías Esperar todo. Pero al esperar las cosas que la prueba de unidad específica está buscando, su código es mucho más claro porque puede ver de un vistazo cuál es el propósito de la prueba.

Otra ventaja de esto es que estarás un poco más aislado del cambio & recibe mejores mensajes de error cuando un cambio causa un descanso. En otras palabras, si modifica ligeramente alguna parte de su implementación, es más probable que solo se rompa un caso de prueba, lo que le mostrará exactamente lo que está roto, en lugar de un conjunto completo de pruebas que rompan & solo creando ruido.

Editar: Podría ser más claro en base a un ejemplo artificial, donde un objeto calculadora audita todas las adiciones a una base de datos (en pseudo-código) ...

public void CalculateShouldAddTwoNumbersCorrectly() { 
    var auditDB = //Get mock object of Audit DB 
    //Stub out the audit functionality... 
    var calculator = new Calculator(auditDB); 
    int result = calculator.Add(1, 2); 
    //assert that result is 3 
} 

public void CalculateShouldAuditAddsToTheDatabase() { 
    var auditDB = //Get mock object of Audit DB 
    //Expect the audit functionality... 
    var calculator = new Calculator(auditDB); 
    int result = calculator.Add(1, 2); 
    //verify that the audit was performed. 
} 

Así, en el primer caso de prueba que Estamos probando la funcionalidad del método Add & no importa si se produce un evento de auditoría o no, pero sabemos que la calculadora no funcionará sin una referencia de auditDB, así que simplemente lo resuelvo para darnos el mínimo de funcionalidad para que funcione nuestro caso de prueba específico.En la segunda prueba, estamos probando específicamente que cuando realice un Add, se produce el evento de auditoría, por lo que aquí utilizamos las expectativas (observe que ni siquiera nos importa cuál es el resultado, ya que eso no es lo que estamos probando).

Sí, podría combinar las dos cajas en una, & hacer las expectativas y afirmar que su resultado es 3, pero luego está probando dos casos en una prueba de unidad. Esto haría que tus pruebas fuesen más quebradizas (ya que hay una mayor superficie de cosas que podrían cambiar para romper la prueba) y menos claras (ya que cuando falla la prueba fusionada no es inmediatamente obvio cuál es el problema ... si la adición no funciona, o ¿la auditoría no funciona?)

+0

No estoy seguro de cómo es con otros frameworks burlones, pero con NMock, si comienzas a verificar una Expectativa, estarás al acecho de * todas * llamadas externas, lo que a veces crea una carga agobiante. Me doy cuenta de que este requisito nos empuja hacia un mejor diseño, pero en muchos casos, quiero escribir pruebas de unidades para el código heredado en el que no puedo permitirme realizar muchas refacciones en el tiempo antes de enviarlas. –

+2

Hmm ... eso hace la diferencia entonces. Principalmente he usado RhinoMocks, que le permite esperar un método y luego Stub otros. Mis puntos siguen en pie (vea mi ejemplo agregado), pero en su caso, supongo que tendrá que hacer todas sus pruebas basadas en expectativas (es decir, pruebas de comportamiento) en una prueba. – Alconja

+0

Ack, debería haber aclarado (y tal vez quiera mencionar esto en el cuerpo de mi pregunta): también puede mezclar Expects y Stubs en NMock. El problema es que a menudo me gustaría simplemente especificar una Expectativa que ni siquiera tenga que resguardar el resto (de nuevo, esto es particularmente cierto cuando trato con código que podría usar una buena reordenación sólida, cuando hay muchas ocultas). capas de llamadas externas). Oh, y +1. –

1

Bien ... en mi humilde opinión, no puede ser más sencillo: si su prueba se trata de garantizar que su presentador llame a Guardar, haga una espera. Si su prueba se trata de garantizar que su presentador se encargará de la excepción con elegancia si Save vomita, haga un Stub.

Para más detalles, echa un vistazo a this podcast by Hanselman and Osherove (autor de El arte de las pruebas unitarias)

+0

+1 para un buen caso de uso, aunque es muy estrecho, creo que la pregunta todavía podría ser * bastante * un poco más simple. –

+0

De acuerdo. Si insistes que voy a amamantar a ti :) La prueba de la unidad solo debería probar una cosa. Nunca dos. Debería probar si el SUT (Sistema bajo prueba) delega apropiadamente alguna acción a una de sus dependencias. O debería probar si el SUT reacciona adecuadamente a un resultado devuelto por una dependencia. Si primero, usa Mock. Si es segundo, usa Stub. Si tiene dudas, use Jack of Diamonds. – zvolkov

4

"Esperar acciones, consultas". Si la llamada debe cambiar el estado del mundo fuera del objeto bajo prueba, entonces conviértalo en una expectativa: le importa cómo se llama. Si solo se trata de una consulta, puede llamarla una o seis veces sin cambiar el estado del sistema, luego reste la llamada.

Una cosa más, observe que la distinción es entre los stubs y las expectativas, es decir llamadas individuales, no necesariamente objetos enteros.

Cuestiones relacionadas