Respuestas:
Difícil de superar https://github.com/Moq/moq4/wiki/Quickstart
Si eso no es lo suficientemente claro, lo llamaría un error de documentación ...
EDITAR: En respuesta a su aclaración ...
Por cada método simulado Setup
que realice, podrá indicar cosas como:
El .Callback
mecanismo dice "No puedo describirlo en este momento, pero cuando suceda una llamada con esta forma, llámame y haré lo que sea necesario". Como parte de la misma cadena de llamadas fluida, puedes controlar el resultado que se devolverá (si lo hay) a través de .Returns
". En los ejemplos de QS, un ejemplo es que hacen que el valor devuelto aumente cada vez.
En general, no necesitará un mecanismo como este con mucha frecuencia (los patrones de prueba de xUnit tienen términos para antipatrones de la clase Conditional Logic In Tests), y si hay alguna forma más simple o incorporada de establecer lo que necesita, debería ser utilizado con preferencia.
La parte 3 de 4 en la serie Moq de Justin Etheredge lo cubre, y hay otro ejemplo de devoluciones de llamada aquí.
Puede encontrar un ejemplo simple de devolución de llamada en Uso de devoluciones de llamada con publicación de Moq .
Callback
no tiene nada que ver con el valor de retorno (a menos que lo vincules a través del código). Básicamente, solo se asegura de que se llame a la devolución de llamada antes o después de cada invocación (dependiendo de si la encadenó antes o después Returns
respectivamente), simple y llanamente.
A continuación, se muestra un ejemplo del uso de una devolución de llamada para probar una entidad enviada a un servicio de datos que maneja una inserción.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Sintaxis alternativa del método genérico:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Entonces puedes probar algo como
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
It.Is<T>
in a en Mock.Verify
lugar de ensuciar la prueba con temps. Pero +1 porque apuesto a que hay mucha gente que trabajará mejor con un ejemplo.
Hay dos tipos de Callback
en Moq. Uno ocurre antes de que vuelva la llamada; el otro ocurre después de que regresa la llamada.
var message = "";
mock.Setup(foo => foo.Execute(arg1: "ping", arg2: "pong"))
.Callback((x, y) =>
{
message = "Rally on!";
Console.WriteLine($"args before returns {x} {y}");
})
.Returns(message) // Rally on!
.Callback((x, y) =>
{
message = "Rally over!";
Console.WriteLine("arg after returns {x} {y}");
});
En ambas devoluciones de llamada, podemos:
Callback
es simplemente un medio para ejecutar cualquier código personalizado que desee cuando se realiza una llamada a uno de los métodos de la simulación. He aquí un ejemplo sencillo:
public interface IFoo
{
int Bar(bool b);
}
var mock = new Mock<IFoo>();
mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
.Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
.Returns(42);
var ret = mock.Object.Bar(true);
Console.WriteLine("Result: " + ret);
// output:
// Bar called with: True
// Result: 42
Recientemente me encontré con un caso de uso interesante para él. Suponga que espera algunas llamadas a su simulacro, pero ocurren al mismo tiempo. Por lo tanto, no tiene forma de saber el orden en el que se llamarían, pero desea saber las llamadas que esperaba que se realizaran (independientemente del orden). Puedes hacer algo como esto:
var cq = new ConcurrentQueue<bool>();
mock.Setup(f => f.Bar(It.IsAny<bool>())).Callback<bool>(cq.Enqueue);
Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false));
Console.WriteLine("Invocations: " + String.Join(", ", cq));
// output:
// Invocations: True, False
Por cierto, no se confunda con la distinción engañosa "antes Returns
" y "después Returns
". Es simplemente una distinción técnica de si su código personalizado se ejecutará después de Returns
haber sido evaluado o antes. A los ojos de la persona que llama, ambos se ejecutarán antes de que se devuelva el valor. De hecho, si el método es void
-returning, ni siquiera puede llamar Returns
y, sin embargo, funciona igual. Para obtener más información, consulte https://stackoverflow.com/a/28727099/67824 .
Además de las otras buenas respuestas aquí, lo he usado para realizar lógica antes de lanzar una excepción. Por ejemplo, necesitaba almacenar todos los objetos que se pasaron a un método para su posterior verificación, y ese método (en algunos casos de prueba) necesitaba lanzar una excepción. Llamando .Throws(...)
en Mock.Setup(...)
anula la Callback()
acción y nunca se lo llama. Sin embargo, al lanzar una excepción dentro de la devolución de llamada, aún puede hacer todas las cosas buenas que una devolución de llamada tiene para ofrecer y aún así lanzar una excepción.