Autofixture: ¿Deshabilitar opcionalmente el almacenamiento en caché para AutoMoq?

Creado en 20 may. 2016  ·  14Comentarios  ·  Fuente: AutoFixture/AutoFixture

Cuando se usa un AutoConfiguredMoqCustomization, el valor devuelto por el accesorio usado se almacena en caché, y se devolverá el mismo valor en cada invocación (no estoy seguro si esto se hace en MockType.ReturnsUsingContext o en otro lugar, pero veo un comentario al respecto en la línea 104 allí).

La única forma que he encontrado para anularlo es pasar una instancia Frozen del simulacro en cuestión al método de prueba y anular la configuración con una función que invoca el dispositivo:

someMock.Setup(it => it.SomeMethodReturningAString()).Returns(() => fixture.Create<string>())

¿Hay alguna forma más genérica / inteligente de configurar los simulacros generados automáticamente para invocar el dispositivo cada vez que se necesita un valor de AutoFixture, además de configurar manualmente cada invocación?

enhancement good first issue

Comentario más útil

Admito que pensé que los dobles de prueba estaban configurados para volver a llamar a AutoFixture para obtener un valor de retorno. AutoFixture ya tiene un mecanismo de administración de por vida (a través de sus funciones Freeze), por lo que me sorprende un poco que AutoConfiguredMoqCustomization implemente su propio administrador de por vida y, por lo tanto, anule la superficie de control que proporciona AutoFixture.

Todos 14 comentarios

La idea detrás del almacenamiento en caché del valor del resultado era hacer que todos los métodos fueran _puros_ por defecto, lo que se considera una buena práctica.

Si el sistema bajo prueba espera una función pura, pero se le asigna una función impura, es probable que la prueba dé como resultado un falso positivo. P.ej

// This method will return the expected result *if* `GetInt` is pure.
int Double(IDependency dep) {
    return dep.GetInt() + dep.GetInt();
}

Assert.Equal(dep.GetInt * 2, Double(dep));

Por otro lado, si el sistema bajo prueba no asume pureza, darle una función pura o impura no debería hacer ninguna diferencia.

Por lo tanto, el uso predeterminado de funciones puras tenía sentido para mí cuando implementé esta función.

¿Hay alguna forma más genérica / inteligente de configurar los simulacros generados automáticamente para invocar el dispositivo cada vez que se necesita un valor de AutoFixture, además de configurar manualmente cada invocación?

Si necesita hacer esto para el método _un_, pero en varias pruebas, lo pondría en un IConfiguration reutilizable.

Si desea hacer esto para todos los métodos, en múltiples pruebas, necesitará un poco más de trabajo:

  • Defina una nueva implementación de ReturnsUsingContext (IIRC, puede simplemente copiar la implementación actual y eliminar la línea 104)
  • Redefina MockVirtualMethodsCommand y reemplace la llamada a ReturnsUsingContext con una llamada a su nuevo método.
  • Redefina AutoConfiguredMoqCustomization y reemplace la instanciación de MockVirtualMethodsCommand con una instanciación de su nueva clase.

También podríamos pensar en una forma de hacer que este comportamiento sea configurable, pero no estoy seguro de si la demanda es lo suficientemente alta como para justificar esto. En mi opinión, este cambio no debe tomarse a la ligera: la personalización de grano fino puede llevar a una complejidad accidental. @ploeh , ¿qué piensas sobre esto?

Admito que pensé que los dobles de prueba estaban configurados para volver a llamar a AutoFixture para obtener un valor de retorno. AutoFixture ya tiene un mecanismo de administración de por vida (a través de sus funciones Freeze), por lo que me sorprende un poco que AutoConfiguredMoqCustomization implemente su propio administrador de por vida y, por lo tanto, anule la superficie de control que proporciona AutoFixture.

Ese es un muy buen punto, nunca pensé en Freeze esa manera. Cambiar el comportamiento de la personalización ahora sería un cambio rotundo, creo ... ¿crees que debería cambiarse en AutoFixture v4?

¿Es un comportamiento que hemos documentado o "prometido" de alguna manera en las pruebas?

Sorprendentemente, no. Estaba bastante seguro de haber cubierto esto, pero parece que no lo hice. Al menos no pude encontrar ninguna prueba que cubra esto, y eliminar esa línea no provocó que fallara ninguna prueba. Sin embargo, en mi opinión, todavía es un comportamiento observable, y es probable que haya un código que se base en esto ...

Punto justo. ¿Podría ser un valor de configuración que establecemos en un valor ahora y luego lo cambiamos cuando hacemos la transición a AutoFixture 4?

Acordado. En este contexto, ¿qué quiere decir exactamente con "valor de configuración"? ¿Sería suficiente levantarlo a una constante dentro de la clase MockType ? Por ejemplo, private const bool cacheReturnValues = true y luego if(cacheReturnValues) /**/

Fue un comentario un poco descartable, lo admito, así que no sé si sería prácticamente posible sin demasiados cambios. Lo que quise decir, sin embargo, fue esto:

@andreasnilsen quisiera cambiar el comportamiento ahora, pero nos preocupa que sea un cambio radical. Si hacemos que el comportamiento sea configurable, entonces le daremos a @andreasnilsen una forma de cambiar el comportamiento ahora, mientras no rompemos ningún otro cliente.

Cuando presentamos AutoFixture 4, cambiamos la configuración predeterminada, por lo que el comportamiento predeterminado es que los valores de retorno no se almacenen en caché. O quizás simplemente eliminamos esa opción ...

Algo como un cacheReturnValues booleano podría ser una forma de hacerlo, pero tendríamos que permitir que los clientes cambien el valor, por lo que no puede ser private .

(Además, a menos que estemos absolutamente seguros de que solo habrá exactamente dos valores, deberíamos considerar un enum lugar de un bool ).

Ah, primero pensé que te referías a una bandera interna que era fácil de detectar y cambiar en v4.

Los dos primeros enfoques que me vienen a la mente son:

  • Simplemente agregue un parámetro booleano (opcional, para compatibilidad con versiones anteriores) al constructor de AutoConfiguredMoqCustomization , que luego se propagaría a MockVirtualMethodsCommand y al interno MockType.ReturnsUsingContext . Esto parece:

    1. un poco complicado y

    2. también _ad hoc_. Si quisiéramos agregar más parámetros de configuración a la personalización, el constructor se volvería demasiado complejo y difícil de usar / mantener.

  • Utilice el patrón de objeto de parámetro para resolver el problema (ii) anterior.

Soy cauteloso a la hora de tomar decisiones de diseño de las que nos podamos arrepentir y que nos quedemos atascados más adelante, por lo que su experiencia / experiencia en el mantenimiento de este proyecto sería muy apreciada.

Estaba pensando en algo como una estrategia ...

¡Ah, sí, por supuesto! Supongo que estaba obsesionado con un "valor de configuración" y olvidé dar un paso atrás.

Lamento la respuesta tardía, me han abrumado. Lo revisaré pronto y volveré con una propuesta de diseño.

Recientemente, hemos realizado la refactorización de personalización para usar propiedades para el ajuste de personalización. Potencialmente, esta función podría controlarse de la siguiente manera (busque el mejor nombre de configuración):
c# new AutoMoqCustomization { CacheCallResults = false }

Bajo el capó, podríamos implementar eso a través de una estrategia diferente como la que se ha recomendado anteriormente.

Marcar con una etiqueta de salto, ya que debería ser relativamente sencillo implementar esto.

@zvirja, ¿ podrías ayudarme un poco y puedo trabajar en este? Simplemente no sé por dónde empezar :)

@micheleissa ¡ Gracias por tu interés! Sugeriría simplemente leer el código fuente AutoMoq y depurarlo para comprender la mecánica interna. Es muy pequeño, por lo que no debería llevar mucho tiempo ...

Si tiene una pregunta más específica, pregunte 😉

¿Fue útil esta página
0 / 5 - 0 calificaciones