testing - unit - Moq-¿Es posible especificar en una configuración los criterios de verificación(p. Ej., Tiempos llamados)?
moq c# ejemplos (3)
Si necesita configurar un valor de retorno, así como verificar cuántas veces se llamó a la expresión, ¿puede hacer esto en una declaración?
De lo que puedo recopilar, la configuración de Moq (SomeExpression) .Verifiable () llamada junto con Verify (), básicamente hace un Verify (SomeExpression, Times.AtLeastOnce)? Es decir, verifica que la expresión fue llamada solamente.
Aquí hay un ejemplo para explicar mejor la pregunta. Para una interfaz:
interface IFoo
{
int ReturnSomething();
}
¿Los siguientes dos bloques son equivalentes (aparte del primero, Verificará todas las configuraciones marcadas como verificables)?
void Test()
{
var mock = new Mock<IFoo>();
mock.Setup((m) => m.ReturnSomething()).Returns(1).Verifiable();
mock.Verify();
}
y
void Test()
{
var mock = new Mock<IFoo>();
mock.Setup((m) => m.ReturnSomething()).Returns(1);
mock.Verify((m) => m.ReturnSomething(), Times.AtLeastOnce());
}
Si quisiera verificar el número de llamadas (por ejemplo, dos veces), ¿es esta la única forma en que la expresión se repite para la configuración y la verificación?
void Test()
{
var mock = new Mock<IFoo>();
mock.Setup((m) => m.ReturnSomething()).Returns(1);
mock.Verify((m) => m.ReturnSomething(), Times.Exactly(2));
}
Simplemente no me gusta tener que llamar a Configuración y Verificar. Bueno, ya que esta es una buena idea para AAA, para reformular, no me gusta tener que repetir la expresión para la configuración y la verificación. En este momento almaceno la expresión en una variable y la paso a cada método, pero no me siento tan limpia.
PD: el contexto para esto es para una comprobación de prueba cuando una caché se actualiza o no (caducidad, etc.)
Al explicar la respuesta de Evren Kuzucuoglu, creé los siguientes métodos de extensión para simplificar un poco la creación de las expresiones:
/// <summary>
/// Creates a method call expression that can be passed to both <see cref="Setup"/> and <see cref="Verify"/>.
/// </summary>
/// <typeparam name="T">Mocked object type.</typeparam>
/// <param name="mock">Mock of <see cref="T"/>.</param>
/// <param name="expression">Method call expression to record.</param>
/// <returns>Method call expression.</returns>
public static Expression<Action<T>> CallTo<T>(this Mock<T> mock, Expression<Action<T>> expression) where T : class
{
return expression;
}
/// <summary>
/// Creates a method call expression that can be passed to both <see cref="Setup"/> and <see cref="Verify"/>.
/// </summary>
/// <typeparam name="T">Mocked object type.</typeparam>
/// <typeparam name="TResult">Method call return type.</typeparam>
/// <param name="mock">Mock of <see cref="T"/>.</param>
/// <param name="expression">Method call expression to record.</param>
/// <returns>Method call expression.</returns>
public static Expression<Func<T, TResult>> CallTo<T, TResult>(this Mock<T> mock, Expression<Func<T, TResult>> expression) where T : class
{
return expression;
}
Ejemplo de uso:
var createMapperCall = mockMappingFactory.CallTo(x => x.CreateMapper());
mockMappingFactory.Setup(createMapperCall).Returns(mockMapper.Object);
mockMappingFactory.Verify(createMapperCall, Times.Once());
Para contestar la primera pregunta, sí, los dos bloques son equivalentes. Ambos fallarán cuando se .Verify
si .Verify
se llamó al método en el simulacro.
No se puede especificar la verificación por adelantado, que yo sepa, y si lo piensa, tiene sentido.
Esto es especificando el comportamiento del simulacro:
mock.Setup(m => m.ReturnSomething()).Returns(1);
Esto es verificar el comportamiento de la persona que llama:
mock.Verify(m => m.ReturnSomething(), Times.AtLeastOnce());
Personalmente, prefiero llamar a verificar individualmente para confirmar el comportamiento requerido de la persona que llama, el .Verifiable()
y .Verify()
son accesos directos que son menos estrictos (solo verifican el método que se llamó una o más veces) sin embargo, si conoce su código solo se debe llamar a un método una vez, poner la verificación al final para confirmarla.
Comencé a hacer eso después de que una combinación de códigos dio como resultado que se llamara dos veces a un método, la prueba aún pasaba desde que se llamó al menos una vez, pero también significaba que algo más sucedió varias veces, ¡lo cual no debería haber ocurrido!
Tengo este problema todo el tiempo. Uso simulacros estrictos, y quiero especificar estrictamente (es decir, utilicé It.Is<>()
lugar de It.IsAny()
), así como verificar estrictamente (es decir, especificar los tiempos). Lamentablemente, no se puede usar verificable, porque a Moq le falta una sobrecarga Verifiable(Times)
.
La expresión completa de la llamada, incluida It.Is<>()
es generalmente grande. Así que para evitar la duplicación generalmente recurro a lo siguiente:
Expression<Action<MockedType>> expression = mockedTypeInstance => mockedTypeInstance.MockedMethod(It.Is<TFirstArgument>(firstArgument => <some complex statement>)/*, ...*/);
_mock.Setup(expression);
/* run the test*/
_mock.Verify(expression, Times.Once);
No es extremadamente legible, pero no creo que haya otra forma de utilizar la configuración estricta y la verificación estricta.