Utilisation de Moq pour déterminer si une méthode est appelée

159

Je crois comprendre que je peux tester qu'un appel de méthode se produira si j'appelle une méthode de niveau supérieur, c'est-à-dire:

public abstract class SomeClass()
{    
    public void SomeMehod()
    {
        SomeOtherMethod();
    }

    internal abstract void SomeOtherMethod();
}

Je veux tester que si j'appelle, SomeMethod()je m'attends à ce que SomeOtherMethod()cela soit appelé.

Ai-je raison de penser que ce type de test est disponible dans un cadre moqueur?

Owen
la source

Réponses:

186

Vous pouvez voir si une méthode dans quelque chose que vous avez simulé a été appelée en utilisant Verify, par exemple:

static void Main(string[] args)
{
        Mock<ITest> mock = new Mock<ITest>();

        ClassBeingTested testedClass = new ClassBeingTested();
        testedClass.WorkMethod(mock.Object);

        mock.Verify(m => m.MethodToCheckIfCalled());
}

class ClassBeingTested
{
    public void WorkMethod(ITest test)
    {
        //test.MethodToCheckIfCalled();
    }
}

public interface ITest
{
    void MethodToCheckIfCalled();
}

Si la ligne est laissée commentée, elle lèvera une exception MockException lorsque vous appelez Verify. S'il n'est pas commenté, il passera.

Paul
la source
7
C'est la bonne réponse. Vous devez cependant comprendre quelque chose. Vous NE POUVEZ PAS vous moquer d'une méthode / propriété qui n'est ni abstraite ni virtuelle (évidemment, toutes les méthodes et propriétés d'interface peuvent être simulées).
25
-1: Le .Expect (...). Verifiable () est redondant dans ce code. En utilisant AAA, la vérification que vous avez est parfaite. .Verifiable est à utiliser avec .Verify () i, .e. la version sans argument. Voir stackoverflow.com/questions/980554/…
Ruben Bartelink
@ I-- yes it can
reggaeguitar
6

Non, les tests simulés supposent que vous utilisez certains modèles de conception testables, dont l'injection. Dans votre cas, vous testeriez SomeClass.SomeMethod et SomeOtherMethoddevez être implémenté dans une autre entité qui doit être interfacée.

Votre Someclassconstructeur ressemblerait à New(ISomeOtherClass). Ensuite, vous vous moquez de l' ISomeOtherClassattente et définissez l'attente sur son SomeOtherMethodappel et vérifiez l'attente.

Val
la source
0

Même si je suis d'accord pour dire que la réponse de @ Paul est la voie recommandée, je veux juste ajouter une voie alternative qui est fournie par moqle soi.

Puisqu'il SomeClassest abstracten effet moquable, mais public void SomeMehod()ne l'est pas. Le but est de trouver le moyen de se moquer et d'appeler d'une manière ou d'une autre cette méthode, puis d'utiliser CallBasepropager l'appel au SomeOtherMethod(). Cela peut sembler un piratage, mais c'est simple par essence. Il pourrait être utilisé dans le cas où le refactoring proposé n'est pas possible.

// This class is used only for test and purpose is make SomeMethod mockable
public abstract class DummyClass : SomeClass
{
    public virtual void DummyMethod() => base.SomeMethod();
}

Ensuite, vous pouvez configurer DummyMethod()pour propager l'appel en définissant un CallBaseindicateur.

//Arrange
var mock = new Mock<DummyClass>();
mock.Setup(m => m.DummyMethod()).CallBase();

//Act
mock.Object.SomeMethod();

//Assert
mock.Verify(m => m.SomeOtherMethod(), Times.Once);
Johnny
la source
Évalué parce que c'est plus compliqué et nécessite un passe
partout
voté pour parce que parfois vous ne pouvez pas refactoriser et vous devez tester l'implémentation telle quelle
wickdninja