50 votes

Moq : Configurer une méthode fantaisie pour qu'elle échoue au premier appel et réussisse au second.

Quelle est la manière la plus succincte d'utiliser Moq pour simuler une méthode qui lèvera une exception la première fois qu'elle sera appelée, puis réussira la deuxième fois qu'elle sera appelée ?

9 votes

Moq 4.2 et plus supporte maintenant ceci via SetupSequence() .

62voto

rsbarro Points 12575

J'utiliserais Callback et incrémenter un compteur pour déterminer s'il faut ou non lever une exception à partir de Callback .

[Test]
public void TestMe()
{
    var count = 0;
    var mock = new Mock<IMyClass>();
    mock.Setup(a => a.MyMethod()).Callback(() =>
        {
            count++;
            if(count == 1)
                throw new ApplicationException();
        });
    Assert.Throws(typeof(ApplicationException), () => mock.Object.MyMethod());
    Assert.DoesNotThrow(() => mock.Object.MyMethod());
}

public interface IMyClass
{
    void MyMethod();
}

0 votes

C'est une bonne réponse et cette approche devient préférable dès que l'on veut que plus de deux choses se produisent.

0 votes

Cette approche est simple, ce qui est bien, mais regardez le post de Phil Haack dans la réponse de @Mathias. C'est plutôt astucieux. En utilisant une méthode d'extension, vous pouvez faire quelque chose comme reader.Setup(r => r.Read()).ReturnsInOrder(true, true, false);

4 votes

La fonction ReturnsInOrder() est intéressante si vous voulez renvoyer une séquence de valeurs, mais il n'était pas évident de savoir comment l'utiliser pour lancer une exception dans le cadre de la séquence.

47voto

aghidini Points 1802

À partir de Moq 4.2, vous pouvez simplement utiliser la méthode intégrée SetupSequence() (comme indiqué dans le commentaire de @RichardBarnett).

Exemple :

var mock = new Mock<IMyClass>();
mock.SetupSequence(x => x.MyMethod("param1"))
    .Throws<MyException>()
    .Returns("test return");

1 votes

Cela ne fonctionne que si MyMethod a un type de retour. Si la méthode est void alors vous ne pouvez pas utiliser cette approche. Ce serait bien s'il y avait un moyen de le faire avec void méthodes

18voto

anthony Points 15067

La meilleure solution que j'ai trouvée jusqu'à présent est la suivante :

interface IFoo
{
    void Bar();
}

[Test]
public void TestBarExceptionThenSuccess()
{
    var repository = new MockRepository(MockBehavior.Default);
    var mock = repository.Create<IFoo>();

    mock.Setup(m => m.Bar()).
        Callback(() => mock.Setup(m => m.Bar())). // Setup() replaces the initial one
        Throws<Exception>();                      // throw an exception the first time

    ...
}

2 votes

+1 C'est une approche cool. C'est juste que ce n'est peut-être pas clair pour quelqu'un qui trébuche dans le code ce qui se passe ici.

0 votes

Bien joué, je n'y avais pas pensé !

5 votes

Notez que le remplacement de l'objet fantaisie efface l'historique, vous ne pouvez donc pas faire mock.Verify(m => m.Bar(), Times.Exactly(2)) .

6voto

Mathias Points 8040

Phil Haack a une intéressante article de blog sur la configuration d'une méthode pour retourner une séquence particulière de résultats. Il semble que ce serait un bon point de départ, avec un peu de travail, car au lieu d'une séquence de valeurs d'un certain type, il faudrait maintenant avoir une séquence de résultats qui pourrait être de type T, ou une exception.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X