unit test setup net mock example unit-testing mocking

unit testing - test - ¿Son los burlas mejores que los talones?



mockito mock (6)

Hace un tiempo leí el artículo de Mocks Are not Stubs de Martin Fowler y debo admitir que tengo un poco de miedo a las dependencias externas con respecto a la complejidad añadida, así que me gustaría preguntar:

¿Cuál es el mejor método para usar cuando se realizan pruebas unitarias?

¿Es mejor usar siempre un marco simulado para simular automáticamente las dependencias del método que se está probando, o preferiría usar mecanismos más simples como, por ejemplo, talones de prueba?


Como dice el mantra: "Ve con lo más simple que pueda funcionar".

  1. Si las clases falsas pueden hacer el trabajo, acompáñelos.
  2. Si necesita una interfaz con varios métodos para burlarse, vaya con un marco simulado.

Evite usar simulaciones siempre porque hacen que las pruebas sean frágiles. Sus pruebas ahora tienen un conocimiento intrincado de los métodos invocados por la implementación, si la interfaz simulada o su implementación cambian ... sus pruebas se rompen. Esto es malo porque pasarás más tiempo haciendo que tus pruebas se ejecuten en lugar de solo ejecutar tu SUT. Las pruebas no deben ser inapropiadamente íntimas con la implementación.
Así que usa tu mejor juicio. Prefiero burlas cuando esto me ayude a ahorrarme la escritura, actualizando una clase falsa con n >> 3 métodos.

Actualizar Epílogo / Deliberación:
(Gracias a Toran Billups, por ejemplo, de una prueba de simulacro. Ver a continuación)
Hola Doug: Bueno, creo que hemos trascendido a otra guerra santa: los TDD clásicos frente a los TDD Mockist. Creo que soy del primero.

  • Si estoy en la prueba # 101 Test_ExportProductList y encuentro que necesito agregar un nuevo param a IProductService.GetProducts (). Yo hago eso para que esta prueba sea verde. Utilizo una herramienta de refactorización para actualizar todas las demás referencias. Ahora descubro que todas las pruebas de simulacros que llaman a este miembro ahora explotan. Luego tengo que volver atrás y actualizar todas estas pruebas: una pérdida de tiempo. ¿Por qué falló ShouldPopulateProductsListOnViewLoadWhenPostBackIsFalse? ¿Fue porque el código está roto? Más bien, las pruebas están rotas. Yo prefiero la falla de una prueba = 1 lugar para arreglar . Burlarse de las frecuencias va en contra de eso. ¿Los stubs serían mejores? Si tuviera un fake_class.GetProducts () .. sure Un lugar para cambiar en lugar de la cirugía de escopeta en múltiples llamadas a Espera. Al final, es una cuestión de estilo ... si tuvieras un método de utilidad común MockHelper.SetupExpectForGetProducts (), eso también sería suficiente ... pero verás que esto no es común.
  • Si coloca una franja blanca en el nombre de la prueba, la prueba es difícil de leer. Gran parte del código de plomería para el marco simulado oculta la prueba real que se está realizando.
  • requiere que aprendas este sabor particular de un marco de burla

Lea la discusión de Luke Kanies sobre exactamente esta pregunta en esta publicación de blog . Hace referencia a una publicación de Jay Fields que incluso sugiere que usar [un equivalente de ruby''s / mocha''s] stub_everything es preferible para que las pruebas sean más robustas. Para citar las últimas palabras de Fields: "Mocha hace que sea tan fácil definir un simulacro como definir un talón, pero eso no significa que siempre deba preferir los simulacros. De hecho, generalmente prefiero los talones y uso simulaciones cuando sea necesario. "


Lo mejor es usar una combinación, y tendrás que usar tu propio juicio. Aquí están las pautas que uso:

  • Si hacer una llamada al código externo es parte del comportamiento esperado (hacia afuera) de su código, esto debe ser probado. Usa un simulacro
  • Si la llamada es realmente un detalle de implementación que al mundo exterior no le importa, prefiera los apéndices. Sin embargo:
  • Si te preocupa que las implementaciones posteriores del código probado puedan pasar accidentalmente por tus talones, y quieres darte cuenta si eso sucede, utiliza un simulacro. Estás uniendo tu prueba a tu código, pero es por el solo hecho de darte cuenta de que tu stub ya no es suficiente y que tu prueba debe volver a funcionar.

El segundo tipo de simulacro es una especie de mal necesario. Realmente, lo que está sucediendo aquí es que ya sea que uses un stub o un simulacro, en algunos casos debes unir tu código más de lo que te gustaría. Cuando eso sucede, es mejor usar un simulacro que un talón solo porque sabrá cuándo se rompe ese acoplamiento y su código ya no se escribirá de la manera que su prueba pensó que sería. Probablemente sea mejor dejar un comentario en la prueba cuando hagas esto para que quien lo rompa sepa que su código no está mal, la prueba sí.

Y de nuevo, este es un olor a código y un último recurso. Si encuentra que necesita hacer esto a menudo, intente repensar la forma en que escribe sus pruebas.


No importa Statist vs. Interaction. Piensa en los Roles y las Relaciones. Si un objeto colabora con un vecino para hacer su trabajo, entonces esa relación (como se expresa en una interfaz) es un candidato para realizar pruebas usando simulaciones. Si un objeto es un objeto de valor simple con un poco de comportamiento, entonces pruébelo directamente. No veo el sentido de escribir simulacros (o incluso talones) a mano. Así es como todos comenzamos y refactorizamos lejos de eso.

Para una discusión más larga, considere echar un vistazo a http://www.mockobjects.com/book


Por lo general, prefiero usar simulaciones debido a las expectativas. Cuando llama a un método en un stub que devuelve un valor, por lo general solo le devuelve un valor. Pero cuando llamas a un método en un simulacro, no solo devuelve un valor, sino que también impone la expectativa de que configuraste que incluso se llamó al método en primer lugar. En otras palabras, si configura una expectativa y luego no llama a ese método, se genera una excepción. Cuando establece una expectativa, básicamente está diciendo "Si este método no se llama, algo salió mal". Y lo opuesto es cierto, si llamas a un método en un simulacro y no estableces una expectativa, emitirá una excepción, básicamente diciendo "Oye, ¿qué haces llamando a este método cuando no lo esperabas?".

A veces no quieres expectativas en cada método al que llamas, por lo que algunos frameworks burlones permitirán burlas "parciales" que son como híbridos falsos / trozos, porque solo se cumplen las expectativas que estableces, y se trata cualquier otra llamada a método. más como un talón en el sentido de que simplemente devuelve un valor.

Sin embargo, un lugar válido para usar stubs que puedo pensar en la parte superior es cuando estás introduciendo las pruebas en el código heredado. A veces es más fácil crear un apéndice subclasificando la clase que estás probando que refacturando todo para hacer que la burla sea fácil o incluso posible.

Y a esto ...

Evite usar simulaciones siempre porque hacen que las pruebas sean frágiles. Sus pruebas ahora tienen un conocimiento complejo de los métodos llamados por la implementación, si la interfaz simulada cambia ... sus pruebas se rompen. Entonces usa tu mejor juicio .. <

... Digo si mi interfaz cambia, mis pruebas deberían romperse. Porque el objetivo de las pruebas unitarias es que prueban con precisión mi código tal como existe en este momento.


Solo depende del tipo de prueba que estés haciendo. Si realiza pruebas basadas en el comportamiento, es posible que desee un simulacro dinámico para poder verificar que se produce alguna interacción con su dependencia. Pero si está haciendo pruebas basadas en el estado, es posible que desee un código auxiliar para verificar los valores / etc.

Por ejemplo, en la prueba siguiente, observa que anulo la vista para poder verificar que el valor de una propiedad esté establecido (prueba basada en estado). Luego creo un simulacro dinámico de la clase de servicio para asegurarme de que se llama un método específico durante la prueba (prueba basada en la interacción / comportamiento).

<TestMethod()> _ Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False() mMockery = New MockRepository() mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView) mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService) mPresenter = New ProductPresenter(mView, mProductService) Dim ProductList As New List(Of Product)() ProductList.Add(New Product()) Using mMockery.Record() SetupResult.For(mView.PageIsPostBack).Return(False) Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once() End Using Using mMockery.Playback() mPresenter.OnViewLoad() End Using ''Verify that we hit the service dependency during the method when postback is false Assert.AreEqual(1, mView.Products.Count) mMockery.VerifyAll() End Sub