2009-09-13 7 views
15

Cela semble être quelque chose de simple mais je n'arrive pas à le faire fonctionner.Comment vérifier qu'une autre méthode de la classe a été appelée à l'aide de Moq

J'ai une classe avec une méthode Save qui appelle simplement une autre méthode ShouldBeCalled(). Je veux vérifier que si j'appelle Save() que l'autre méthode ShouldBeCalled() est exécutée au moins une fois. Je pensais que je pourrais faire ce qui suit.

public class ClassA 
{ 
    public virtual void Save() 
    { 
     ShouldBeCalled(); 
    } 

    public virtual void ShouldBeCalled() 
    { 
     //This should get executed 
    } 
} 

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_Should_Call_ShouldBeCalled() 
    { 
     var mockClassA = new Mock<ClassA>(); 
     mockClassA.Object.Save(); 

     mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce()); 
    } 
} 

Mais je reçois l'exception « invocation sur la maquette Expected au moins une fois, mais n'a jamais été effectuée: x => x.ShouldBeCalled() »

Il est juste une supposition, mais est-Moq Redéfinition de la Save() méthode avec sa propre version qui ignore tout ce que j'ai dans la méthode Save() de l'objet réel.

Répondre

28

Vous rencontrez ce problème car vous vous moquez de ce que vous testez. Cela n'a pas de sens.

Vous avez raison de dire que Moq remplacera l'implémentation de votre méthode par la sienne. La raison en est que vous êtes supposé utiliser Moq pour se moquer de la classe que vous testez appels, pas la classe que vous testez lui-même.

Ce test serait approprié si votre code a été conçu ainsi:

public class ClassA 
{ 
    BusinessLogicClass bl; 
    public ClassA(BusinessLogicClass bl) 
    { 
     this.bl = bl; 
    } 

    public void Save() 
    { 
     bl.ShouldBeCalled(); 
    } 
} 

public class BusinessLogicClass 
{ 
    public virtual void ShouldBeCalled() 
    { 
     //This should get executed 
    } 
} 

Et voici le test correct de cette méthode maintenant:

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_ShouldCallShouldBeCalled() 
    { 
     //Arrange 
     var mockBLClass = new Mock<BusinessLogicClass>(); 
     mockBLClass.Setup(x => x.ShouldBeCalled()).Verifyable(); 

     //Act  
     ClassA classA = new ClassA(mockBLClass.Object); 
     classA.Save(); 

     //Assert 
     mockBLClass.VerifyAll(); 
    } 
} 

La leçon clé ici est que vous maquette/stub ce que votre test doit exécuter, pas ce que vous testez lui-même.

Hope this helps, Anderson

+0

+1: Grande réponse, complète avec les exemples de dépendance et de code! Notez qu'une alternative à la méthode virtuelle serait d'ajouter une interface IBusinessLogic et de la transmettre. – TrueWill

+0

L'interface IBusinessLogic est certainement le moyen d'aller ici, mais je ne voulais pas m'y plonger trop profondément. –

+1

Merci pour la bonne réponse. J'ai eu le sentiment que j'essayais de faire quelque chose de mal avec mon approche et maintenant je sais que je l'étais :) – Adam

2

Oui, cela peut être fait. Cependant, vous devez ajouter une ligne de code pour que Moq suive si la méthode ShouldBeCalled a bien été appelée.

Quelque chose comme ce qui suit fonctionnera:

var mockClassA = new Mock<ClassA>(); 
mockClassA.Setup(x => x.ShouldBeCalled()).Verifiable();  
mockClassA.Object.Save();  
mockClassA.Verify(x => s.ShouldBeCalled(), Times.AtLeastOnce()); 

La méthode de configuration définit les attentes. Lorsque vous appelez Verify, vous demandez à Moq de vérifier ces attentes. Si vous n'effectuez pas d'appel d'installation pour créer des attentes pour la méthode ShouldBeCalled, Moq ne considère pas qu'il soit traçable et échouera donc fortement lorsque vous essaierez de le vérifier.

+0

J'ai essayé ce qui précède mais je vois toujours la même erreur. Ce que vous dites est logique, c'est pourquoi je n'arrive pas à comprendre pourquoi cela ne fonctionne pas. – Adam

+0

Pouvez-vous modifier la déclaration d'installation pour simuler un retour? Par exemple, mockClassA.Setup (x => x.ShouldBeCalled()). Retourne (...). –

+0

La configuration est redondante ici car vous n'utilisez pas Strict. Mais +1 en général –

4

Essayez d'utiliser la CallBase = true et false. J'ai couru votre code et cela fonctionne.

var mockClassA = new Mock<ClassA>(); 
mockClassA.CallBase = true; 
mockClassA.Object.Save(); 
mockClassA.CallBase = false; 
mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce()); 
+0

Cela fonctionne, wow! Je suis nouveau à Moq, donc je ne sais pas si c'est une bonne pratique ou pas, mais ça marche. Notez que cela ne fonctionne que si vous vous moquez d'une classe concrète avec des méthodes virtuelles, comme c'est le cas avec ClassA. Si la classe implémente une interface et que vous créez le simulacre à partir de l'interface, cela ne fonctionnera pas. Par exemple, si ClassA implémentait IClassA, la première ligne de la réponse d'Eric deviendrait "var mockClassA = new Mock ();" En se moquant de l'interface de cette façon, le test échouera car ShouldBeCalled n'est jamais appelé. Problème très similaire au code original dans la question d'Adam. –

1

Vous pouvez stub méthodes dans le système en cours de test en utilisant CallBase.

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_Should_Call_ShouldBeCalled() 
    { 
     // Arrange 
     var mockClassA = new Mock<ClassA>(); 
     mockClassA.CallBase = true; // this will call real methods unless the method is mocked/stubbed. 
     mockClassA.Setup(a => a.ShouldBeCalled()); 

     // Act 
     mockClassA.Save(); 

     // Assert 
     mockClassA.Verify(a => a.ShouldBeCalled(), Times.Once()); 
    } 
} 
+0

l'hyperlien dans cette réponse n'est plus à jour. – buffjape

Questions connexes