setup - unit test c# ejemplo
Verificando un parámetro especĂfico con Moq (4)
public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
var messageServiceClientMock = new Mock<IMessageServiceClient>();
var queueableMessage = CreateSingleQueueableMessage();
var message = queueableMessage[0];
var xml = QueueableMessageAsXml(queueableMessage);
messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(xml)).Verifiable();
//messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();
var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(essageServiceClientMock.Object);
var loggerStub = new Mock<ILogger>();
var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});
//messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(xml), Times.Once());
messageServiceClientMock.Verify();
}
Estoy empezando a usar Moq y estoy luchando un poco. Estoy intentando verificar que messageServiceClient reciba el parámetro correcto, que es un XmlElement, pero no puedo encontrar la forma de hacerlo funcionar. Funciona solo cuando no controlo un valor particular.
¿Algunas ideas?
Respuesta parcial: he encontrado una manera de probar que el xml enviado al proxy es correcto, pero todavía no creo que sea la manera correcta de hacerlo.
public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
var messageServiceClientMock = new Mock<IMessageServiceClient>();
messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();
var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(messageServiceClientMock.Object);
var loggerStub = new Mock<ILogger>();
var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
var message = CreateMessage();
client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});
messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(It.Is<XmlElement>(xmlElement => XMLDeserializer<QueueableMessage>.Deserialize(xmlElement).Messages.Contains(message))), Times.Once());
}
Por cierto, ¿cómo podría extraer la expresión de la llamada Verify?
Creo que el problema en el hecho de que Moq verificará la igualdad. Y, dado que XmlElement no anula Equals, su implementación comprobará la igualdad de referencia.
¿No puedes usar un objeto personalizado, por lo que puedes anular iguales?
He estado verificando llamadas de la misma manera, creo que es la forma correcta de hacerlo.
mockSomething.Verify(ms => ms.Method(
It.IsAny<int>(),
It.Is<MyObject>(mo => mo.Id == 5 && mo.description == "test")
), Times.Once());
Si su expresión lambda se vuelve difícil de manejar, puede crear una función que tome MyObject
como entrada y MyObject
true
/ false
...
mockSomething.Verify(ms => ms.Method(
It.IsAny<int>(),
It.Is<MyObject>(mo => MyObjectFunc(mo))
), Times.Once());
private bool MyObjectFunc(MyObject myObject)
{
return myObject.Id == 5 && myObject.description == "test";
}
Además, tenga en cuenta un error con Mock, donde el mensaje de error indica que el método fue llamado varias veces cuando no se llamó. Puede que ya lo hayan solucionado, pero si ve ese mensaje, puede considerar verificar que el método fue realmente llamado.
EDITAR: Aquí hay un ejemplo de verificación de llamada varias veces para aquellos escenarios en los que desea verificar que llame a una función para cada objeto en una lista (por ejemplo).
foreach (var item in myList)
mockRepository.Verify(mr => mr.Update(
It.Is<MyObject>(i => i.Id == item.Id && i.LastUpdated == item.LastUpdated),
Times.Once());
El mismo enfoque para la configuración ...
foreach (var item in myList) {
var stuff = ... // some result specific to the item
this.mockRepository
.Setup(mr => mr.GetStuff(item.itemId))
.Returns(stuff);
}
Así que cada vez que se solicite GetStuff para ese itemId, devolverá cosas específicas a ese artículo. Alternativamente, puede usar una función que toma itemId como entrada y devuelve cosas.
this.mockRepository
.Setup(mr => mr.GetStuff(It.IsAny<int>()))
.Returns((int id) => SomeFunctionThatReturnsStuff(id));
Otro método que vi en un blog hace un tiempo (¿quizás Phil Haack?) Tenía una configuración que regresaba de algún tipo de objeto de dequeue: cada vez que se llamaba a la función, extraía un elemento de una cola.
Si la lógica de verificación no es trivial, será complicado escribir un método lambda grande (como muestra su ejemplo). Podría poner todas las declaraciones de prueba en un método separado, pero no me gusta hacer esto porque interrumpe el flujo de lectura del código de prueba.
Otra opción es usar una devolución de llamada en la llamada de Configuración para almacenar el valor que se pasó al método simulado, y luego escribir los métodos Assert
estándar para validarlo. Por ejemplo:
// Arrange
MyObject saveObject;
mock.Setup(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()))
.Callback<int, MyObject>((i, obj) => saveObject = obj)
.Returns("xyzzy");
// Act
// ...
// Assert
// Verify Method was called once only
mock.Verify(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()), Times.Once());
// Assert about saveObject
Assert.That(saveObject.TheProperty, Is.EqualTo(2));
Una forma más simple sería hacer:
ObjectA.Verify(
a => a.Execute(
It.Is<Params>(p => p.Id == 7)
)
);