Marco simulado vs marcos de MS Fakes

99

Un poco confundido sobre las diferencias de los frameworks Mock como NMock vs el VS 2011 Fakes Framework. Pasando por MSDN, lo que entiendo es que Fakes le permite burlarse de sus dependencias como RhinoMock o NMock, sin embargo, el enfoque es diferente, Fakes genera código para lograr esta funcionalidad, pero el marco Mocks no. Entonces, ¿es correcto mi entendimiento? ¿Las falsificaciones son solo otro marco simulado?

Nairooz NIlafdeen
fuente

Respuestas:

189

Su pregunta era sobre en qué se diferencia el marco de MS Fakes de NMock y parece que las otras respuestas han resuelto algo de eso, pero aquí hay más información sobre en qué se parecen y en qué se diferencian. NMock también es similar a RhinoMocks y Moq, así que los estoy agrupando con NMock.

Hay 3 diferencias principales que veo de inmediato entre NMock / RhinoMocks / Moq y MS Fakes Framework:

  • El marco de falsificaciones de MS utiliza código generado, al igual que Accessors en versiones anteriores de Visual Studio en lugar de tipos genéricos. Cuando desee utilizar el marco de falsificaciones para una dependencia, agregue el ensamblado que contiene la dependencia a las referencias del proyecto de prueba y luego haga clic derecho sobre él para generar los dobles de prueba (stubs o shims). Luego, cuando está probando, en realidad está utilizando estas clases generadas. NMock usa genéricos para lograr lo mismo (es decir IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>()). En mi opinión, el enfoque del marco de MS Fakes inhibe la navegación de código y la refactorización desde dentro de las pruebas, ya que en realidad está trabajando contra una clase generada, no su interfaz real.

  • El marco de falsificaciones de MS proporciona stubs y moles (shims), mientras que NMock, RhinoMocks y Moq proporcionan stubs y simulaciones . Realmente no entiendo la decisión de MS de no incluir simulacros y, personalmente, no soy un fanático de los lunares por las razones que se describen a continuación.

  • Con el marco de falsificaciones de MS, proporciona una implementación alternativa de los métodos que desea apuntar. Dentro de estas implementaciones alternativas, puede especificar los valores de retorno y realizar un seguimiento de la información sobre cómo o si se llamó al método. Con NMock, RhinoMocks y Moq, usted genera un objeto simulado y luego usa ese objeto para especificar valores de retorno stubped o para rastrear interacciones (si los métodos fueron llamados y cómo). Encuentro el enfoque de las falsificaciones de EM más complejo y menos expresivo.

Para aclarar la diferencia en lo que proporcionan los marcos: NMock, RhinoMocks y Moq proporcionan dos tipos de dobles de prueba (stubs y simulacros). El marco de falsificaciones proporciona talones y topos (los llaman calzas), y desafortunadamente no incluye simulaciones. Para comprender las diferencias y similitudes entre NMock y MS Fakes, es útil comprender cuáles son estos diferentes tipos de dobles de prueba:

Stubs: stubs se utilizan cuando necesita proporcionar valores para métodos o propiedades que se le solicitarán a su prueba duplicados por el método bajo prueba. Por ejemplo, cuando mi método bajo prueba llama al método DoesStudentExist () del doble de prueba IStudentRepository, quiero que devuelva verdadero.

La idea de los stubs en NMock y MS fakes es la misma, pero con NMock harías algo como esto:

Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));

Y con MSFakes harías algo como esto:

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
{
    DoesStudentExistInt32 = (studentId) => { return new Student(); }
};

Observe que en el ejemplo de MS Fakes crea una implementación completamente nueva para el método DoesStudentExist (tenga en cuenta que se llama DoesStudentExistInt32 porque el marco de falsificaciones agrega los tipos de datos de parámetros a los nombres de los métodos cuando genera los objetos stub, creo que esto oscurece la claridad de los exámenes). Para ser honesto, la implementación de NMock también me molesta porque usa una cadena para identificar el nombre del método. (Perdóneme si he entendido mal cómo se pretende usar NMock). Este enfoque realmente inhibe la refactorización y por este motivo recomiendo encarecidamente RhinoMocks o Moq sobre NMock.

Simulacros: los simulacros se utilizan para verificar la interacción entre su método bajo prueba y sus dependencias. Con NMock, puede hacer esto estableciendo expectativas similares a esto:

Expect.Once.On(mockStudentRepository).Method("Find").With(123);

Esta es otra razón por la que preferiría RhinoMocks y Moq sobre NMock, NMock usa el estilo de expectativa anterior, mientras que RhinoMocks y Moq admiten el enfoque Arrange / Act / Assert en el que especifica las interacciones esperadas como afirmaciones al final de la prueba de esta manera. :

stubStudentRepository.AssertWasCalled( x => x.Find(123));

Nuevamente, tenga en cuenta que RhinoMocks utiliza una lambda en lugar de una cadena para identificar el método. El marco de ms fakes no proporciona simulaciones en absoluto. Esto significa que en sus implementaciones stub out (consulte la descripción de stubs arriba) debe establecer variables que luego verifique que se hayan configurado correctamente. Eso se vería algo así:

bool wasFindCalled = false;

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() 
{
    DoesStudentExistInt32 = (studentId) => 
        { 
            wasFindCalled = true;
            return new Student(); 
        }
};

classUnderTest.MethodUnderTest();

Assert.IsTrue(wasFindCalled);

Encuentro que este enfoque es un poco complicado, ya que debe rastrear la llamada en el código auxiliar y luego afirmarla más adelante en la prueba. Encuentro que los ejemplos NMock, y especialmente RhinoMocks, son más expresivos.

Moles (Shims): Para ser sincero, no me gustan los lunares, debido a su potencial de mal uso. Una de las cosas que me gustan tanto de las pruebas unitarias (y TDD en particular) es que probar su código le ayuda a comprender dónde ha escrito código deficiente. Esto se debe a que es difícil probar un código mal escrito. Esto no es cierto cuando se usan lunares porque en realidad los lunares están diseñados para permitirle realizar pruebas contra dependencias que no se inyectan o para probar métodos privados. Funcionan de manera similar a los stubs, excepto que usa un ShimsContext como este:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
}

Mi preocupación con las calzas es que la gente comenzará a verlas como "una forma más fácil de realizar pruebas unitarias" porque no te obliga a escribir código de la manera que deberías. Para una descripción más completa de este concepto, vea esta publicación mía:

Para obtener más información sobre algunas preocupaciones relacionadas con los marcos falsos, consulte estas publicaciones:

Si está interesado en aprender RhinoMocks, aquí hay un video de capacitación de Pluralsight (divulgación completa: escribí este curso y recibo regalías por las vistas, pero creo que se aplica a esta discusión, así que lo incluyo aquí):

Jim Cooper
fuente
14
@Jim No estoy de acuerdo con que permitir que uno "pruebe contra dependencias que no se inyectan" sea algo malo. Por el contrario, en la práctica, esta capacidad ayuda a evitar saturar una base de código con muchas interfaces sin sentido y la configuración / complejidad del "cableado de objetos" asociado. Ya he visto algunos proyectos (tanto Java como .NET) que tenían cientos de interfaces Java / C # con una sola clase de implementación y mucha configuración XML inútil (para Spring DI beans, o en Web.config para MS Unity marco de referencia). Es mejor no inyectar tales dependencias .
Rogério
19
@Rogerio, he trabajado en múltiples proyectos con miles de clases que siguen este modelo y cuando la inyección de dependencia se realiza correctamente (si está usando la configuración XML no lo está haciendo bien, en mi humilde opinión), es simple y relativamente indoloro. Por otro lado, he trabajado en sistemas que no hacen esto y el acoplamiento entre clases siempre me ha causado un dolor significativo. Las pruebas no son la razón para usar la inyección de dependencia (aunque no es una mala razón). La verdadera razón es el acoplamiento flojo. Tener interfaces implementadas por una sola clase nunca me ha causado dolor.
Jim Cooper
17
@Jim, veo la capacidad de probar un mal diseño como una gran ventaja. Lo primero que debe hacer antes de refactorizar el código heredado hacia un mejor diseño es escribir pruebas.
Thomas Materna
4
Mi regla es que uso Fakes solo cuando tengo que Shim un método compartido / estático o una clase a la que no tengo acceso para implementar una interfaz. Para todo lo demás, uso Moq. Las falsificaciones son más lentas (porque los ensamblados falsos deben generarse) y se necesita más esfuerzo de codificación para que coincida con la funcionalidad que se obtiene con Moq.
Nick
5
@ CarloV.Dango En primer lugar, sé perfectamente bien que Dependency Injection no requiere interfaces separadas; mi comentario anterior solo aludía a la tendencia a crear tales interfaces cuando se usa DI. En segundo lugar, "IOC" (Inversión de control) no tiene nada que ver con DI. (El primero trata sobre la inversión del flujo de control entre métodos, mientras que el segundo trata sobre la resolución de dependencias para un componente / objeto). Al confundir IOC y DI, es usted quien muestra ignorancia, no yo; y, francamente, este sitio no merece que la gente insulte a otros, así que, por favor, mantengamos la educación.
Rogério
23

Tienes razón, pero hay más en la historia. Las cosas más importantes que se pueden extraer de esta respuesta son:

  1. Su arquitectura debe hacer un uso adecuado de los stubs y la inyección de dependencias, en lugar de depender de la muleta de falsificaciones y simulaciones.

  2. Las falsificaciones y las simulaciones son útiles para probar el código que no debe o no puede cambiar, como:

    • Código heredado que no hace uso (o uso eficiente) de stubs
    • API de terceros
    • Recursos para los que no tiene código fuente

Cuñas (conocido como "Moles", durante el desarrollo) es de hecho un marco de burla que opera mediante llamadas de desvío. En lugar de construir minuciosamente una simulación (sí, ¡incluso usar Moq es relativamente doloroso!), Las calzas simplemente usan el objeto de código de producción que ya está en su lugar. Shims simplemente redirige la llamada desde el destino de producción al delegado de prueba.

Talones se generan a partir de interfaces en el proyecto de destino. El objeto Stub es solo eso: una implementación de la interfaz. El beneficio de usar el tipo Stub es que puede generar rápidamente un stub sin saturar su proyecto de prueba con muchos stubs de un solo uso, sin mencionar la pérdida de tiempo para crearlos. Por supuesto, aún debe crear trozos de hormigón para utilizarlos en muchas pruebas.

La implementación eficiente de las falsificaciones (tipos Shims, Mocks y Stub) requiere un poco de tiempo para acostumbrarse, pero vale la pena el esfuerzo. Personalmente, he ahorrado semanas de tiempo de desarrollo mediante el uso de los tipos Shims / Mole, Mocks y Stub. ¡Espero que te diviertas tanto con la tecnología como yo!

Mike Christian
fuente
11
Votó en contra debido a la falta de detalles y algunos problemas de terminología con sus comentarios sobre las falsificaciones. Falsificaciones es un término general para todos los tipos de dobles de prueba y también el nombre que MS utilizó para su biblioteca de falsificaciones. En su biblioteca, solo Shims son en realidad Moles, los stubs no lo son. Con respecto a la necesidad de ejemplos, me gustaría ver un ejemplo en su respuesta que muestre cómo crear un shim (o stub) con Fakes es más simple que usar Moq. Si realiza cambios en su respuesta para resolverlos, cambiaré mi voto negativo.
Jim Cooper
1
Pero agrega una buena justificación para el uso de suplementos (moles), es decir, código de terceros, API del sistema / SDK. No se trata solo de trabajar con sus propias soluciones internas. Así que te daré un voto para igualar eso :-)
Tony Wall
2
@ Mike y Jim ... Hice un esfuerzo por corregir la terminología utilizada en esta respuesta. Por favor, avíseme si podemos mejorarlo. Gracias.
Snesh
15

Según tengo entendido, el equipo de Visual Studio quería evitar competir con las diversas bibliotecas simuladas disponibles para .NET. La EM a menudo se enfrenta a decisiones difíciles como esta. Están condenados si no brindan cierta funcionalidad ("¿por qué MS no nos proporciona una biblioteca de simulacros; los simulacros son un requisito tan común?") Y condenados si lo hacen ("¿por qué Microsoft está actuando de manera tan agresiva y manejando su ¿Partidarios naturales fuera del mercado? ") Muy a menudo, pero no siempre, deciden abstenerse de simplemente ofrecer su propia alternativa a las tecnologías disponibles y bien recibidas. Ese parece ser el caso aquí.

La función de calce de Fakes es realmente muy útil. Seguro, hay peligros. Se necesita algo de disciplina para asegurarse de que solo use esto cuando sea necesario. Sin embargo, llena un gran vacío. Mi queja principal es que solo se entrega con la edición Ultimate de VS 2012 y, por lo tanto, solo estará disponible para una subsección de la comunidad de desarrollo .NET. Qué pena.

Charles Young
fuente
2
El marco de falsificaciones también está disponible con la edición Premium.
AlwaysAProgrammer
13

Las falsificaciones incluyen dos tipos diferentes de objetos "falsos". El primero, llamado "stub", es esencialmente un maniquí autogenerado cuyo comportamiento predeterminado puede (y normalmente sería) anulado para convertirlo en una simulación más interesante. Sin embargo, carece de algunas de las características que ofrecen la mayoría de los marcos de simulación disponibles actualmente. Por ejemplo, si desea verificar que se invocó un método en una instancia de código auxiliar, deberá agregar la lógica para esto usted mismo. Básicamente, si está creando sus propios simulacros manualmente ahora, los talones probablemente parecerían una mejora. Sin embargo, si ya está utilizando un marco de simulación más completo, es posible que sienta que faltan algunas piezas importantes en los talones de Fakes.

La otra categoría de objeto ofrecida por Fakes, llamada "shim", expone un mecanismo para reemplazar el comportamiento de las dependencias que no han sido (o no pueden) desacoplarse adecuadamente para el reemplazo estándar a través de simulaciones. AFAIK, TypeMock es el único de los principales frameworks de burla que actualmente ofrece este tipo de funcionalidad.

Por cierto, si ha probado Moles antes, Fakes es esencialmente lo mismo, y finalmente sale de Microsoft Research y se convierte en un producto real.

Nicole Calinoiu
fuente
1
Actualización: Moles ahora se ha integrado en MS Fakes. "Fakes Framework en Visual Studio 2012 es la próxima generación de Moles & Stubs. Sin embargo, Fakes es diferente de Moles, por lo que pasar de Moles a Fakes requerirá algunas modificaciones en su código. El marco Moles no será compatible con Visual Studio 2012 . " Fuente: research.microsoft.com/en-us/projects/moles
Sire
1

Con respecto a los objetos falsos (Shim + Stub), se ha definido bien anteriormente, aunque supongo que el último párrafo del último comentario resume bastante bien toda la situación.

Aunque mucha gente argumentará que los objetos falsos (Shim + Stub) son buenos activos para tener en algunos casos de prueba unitaria, la desventaja es que no importa si está utilizando Visual Studio 2012 o Visual Studio 2013, estas opciones SOLO están disponibles con versiones Premium o Ultimate. IOW, esto significa que NO podrá ejecutar NINGUNA de esas falsificaciones (Shim + Stub) en ninguna versión Pro.

Probablemente veas la opción de menú Fakes (Shim + Stub) en las versiones Pro, pero ten cuidado, hay muchas posibilidades de que termines sin absolutamente nada ... No generará ningún error de compilación que te diga algo importante falta, las opciones simplemente no existen, así que no pierdas el tiempo ...

Es un factor importante a considerar en un equipo de desarrollo, especialmente si uno es el único que usa la versión Ultimate mientras que todos los demás usan la versión Pro ... Por otro lado, Moq se puede instalar fácilmente a través de Nuget sin importar qué versión de Visual Studio use. No tuve problemas para usar Moq, la clave con cualquier herramienta es saber para qué se usan y cómo usarlas correctamente;)

SirXamelot
fuente
1
Formatee su pregunta en párrafos más pequeños para que sea más fácil de leer.
@SirXamelot, al no refactorizar el código, no puede cambiar la salida de las llamadas estáticas proporcionadas por .net, por ejemplo, DateTime.Now o Guid.NewGuid
zaitsman