web-dev-qa-db-fra.com

Vérification d'un paramètre spécifique avec Moq

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();
}

Je commence à utiliser Moq et à me battre un peu ... Je tente de vérifier que messageServiceClient reçoit le bon paramètre, qui est un XmlElement, mais je ne trouve aucun moyen de le faire fonctionner. Cela ne fonctionne que lorsque je ne vérifie pas une valeur particulière.

Des idées?

Réponse partielle: J'ai trouvé un moyen de vérifier que le xml envoyé au proxy est correct, mais je ne pense toujours pas que ce soit la bonne façon de le faire.

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());
}

Au fait, comment pourrais-je extraire l'expression de l'appel Verify?

123
Luis Mirabal

Si la logique de vérification est non triviale, il sera difficile d’écrire une méthode lambda volumineuse (comme le montre votre exemple). Vous pouvez placer toutes les instructions de test dans une méthode distincte, mais je n'aime pas le faire car cela perturbe le flux de lecture du code de test. 

Une autre option consiste à utiliser un rappel sur l'appel du programme d'installation pour stocker la valeur transmise à la méthode simulée, puis à écrire des méthodes Assert standard pour la valider. Par exemple:

// 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));
192
Rich Tebb

J'ai vérifié les appels de la même manière - je crois que c'est la bonne façon de le faire.

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => mo.Id == 5 && mo.description == "test")
  ), Times.Once());

Si votre expression lambda devient difficile à manier, vous pouvez créer une fonction qui prend MyObject en entrée et génère 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";
}

En outre, soyez conscient d'un bogue avec Mock dans lequel le message d'erreur indique que la méthode a été appelée plusieurs fois sans être appelée. Ils ont peut-être déjà résolu le problème - mais si vous voyez ce message, vous pouvez vérifier que la méthode a bien été appelée.

EDIT: Voici un exemple d'appel à vérifier plusieurs fois pour les scénarios dans lesquels vous voulez vérifier que vous appelez une fonction pour chaque objet de la liste (par exemple).

foreach (var item in myList)
  mockRepository.Verify(mr => mr.Update(
    It.Is<MyObject>(i => i.Id == item.Id && i.LastUpdated == item.LastUpdated),
    Times.Once());

Même approche pour la configuration ...

foreach (var item in myList) {
  var stuff = ... // some result specific to the item
  this.mockRepository
    .Setup(mr => mr.GetStuff(item.itemId))
    .Returns(stuff);
}

Ainsi, chaque fois que GetStuff est appelé pour cet itemId, il retourne des éléments spécifiques à cet élément. Vous pouvez également utiliser une fonction qui prend itemId en entrée et renvoie des éléments.

this.mockRepository
    .Setup(mr => mr.GetStuff(It.IsAny<int>()))
    .Returns((int id) => SomeFunctionThatReturnsStuff(id));

Une autre méthode que j'avais vue sur un blog il y a quelque temps (Phil Haack peut-être?) Avait été configurée pour revenir d'un type d'objet dequeue - chaque fois que la fonction était appelée, elle allait extraire un élément d'une file d'attente.

69
Mayo

Un moyen plus simple serait de faire:

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);
13
dmitry.sergeyev

Je pense que le problème réside dans le fait que Moq vérifiera l’égalité. Et, puisque XmlElement ne substitue pas Equals, son implémentation vérifiera l’égalité des références. 

Ne pouvez-vous pas utiliser un objet personnalisé pour pouvoir remplacer égal à égal?

1
Fernando

J'en ai eu un aussi, mais le paramètre de l'action était une interface sans propriétés publiques. Nous avons fini par utiliser It.Is () avec une méthode séparée et dans cette méthode, nous avons dû nous moquer de l'interface

public interface IQuery
{
    IQuery SetSomeFields(string info);
}

void DoSomeQuerying(Action<IQuery> queryThing);

mockedObject.Setup(m => m.DoSomeQuerying(It.Is<Action<IQuery>>(q => MyCheckingMethod(q)));

private bool MyCheckingMethod(Action<IQuery> queryAction)
{
    var mockQuery = new Mock<IQuery>();
    mockQuery.Setup(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition())
    queryAction.Invoke(mockQuery.Object);
    mockQuery.Verify(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition(), Times.Once)
    return true
}
0
ds4940