34 votes

Rhino Mocks - Stub .Expect vs .AssertWasCalled

OK, je sais qu'il y a eu beaucoup de confusion sur la nouvelle syntaxe AAA dans Rhino Mocks, mais je dois être honnête, de ce que j'ai vu jusqu'à présent, j'aime. Elle se lit mieux, et permet d'économiser quelques frappes.

En fait, je teste un ListController qui va essentiellement être en charge de certaines listes de choses :) J'ai créé une interface qui deviendra éventuellement le DAL, et c'est bien sûr stubbé pour le moment.

J'avais le code suivant :

( manager est le système à tester, data est l'interface de données stubbed)

    [Fact]
    public void list_count_queries_data()
    {
        data.Expect(x => x.ListCount(1));
        manager.ListCount();
        data.VerifyAllExpectations();
    }

L'objectif principal de ce test est de s'assurer que le gestionnaire interroge effectivement le DAL. Notez que le DAL n'est pas réellement là, donc il n'y a pas de valeur "réelle" qui revient

Cependant, cela échoue car je dois modifier l'attente pour avoir une valeur de retour, comme :

        data.Expect(x => x.ListCount(1)).Return(1);

Cela fonctionnera alors sans problème et le test sera réussi, cependant - ce qui me perturbe, c'est qu'à ce moment-là, la valeur de retour signifie rien . Je peux le changer en 100, 50, 42, n'importe quoi et le test passera toujours ?

Cela me rend nerveux, car un test doit être explicite et doit échouer totalement si les conditions attendues ne sont pas remplies, n'est-ce pas ?

Si je change le test en (le "1" est l'ID attendu auquel le compte est lié) :

    [Fact]
    public void list_count_queries_data()
    {
        manager.ListCount();
        data.AssertWasCalled(x => x.ListCount(1));
    }

Tout passe bien, et si j'allume le test, c'est la tête pour AssertWasNotCalled il échoue comme prévu Je pense aussi qu'il se lit beaucoup mieux, qu'il est plus clair sur ce qui est testé et surtout qu'il réussit et échoue comme prévu !

Donc, Est-ce que je rate quelque chose dans le premier exemple de code ? Que pensez-vous de la création d'assertions sur les stubs (une discussion intéressante a eu lieu à ce sujet) ? aquí J'ai personnellement aimé cette réponse .

1 votes

Merci de m'avoir prévenu. C'est une très vieille question qui s'est perdue dans la poussière.

53voto

Mark Simpson Points 10789

Quel est l'objectif de votre test ?

Quel comportement ou état vérifiez-vous ? En particulier, vérifiez-vous que le collaborateur (les données) a son ListCount méthode appelée (test basé sur l'interaction), ou voulez-vous simplement faire ListCount retourner une valeur fixe pour piloter la classe testée tout en vérifiant le résultat ailleurs (test traditionnel basé sur l'état) ?

Si vous voulez fixer une attente, utilisez un simulacre et une attente : Utilisez MockRepository.CreateMock<IMyInterface>() y myMock.Expect(x => x.ListCount())

Si vous voulez bloquer une méthode, utilisez MockRepository.CreateStub<IMyInterface>() y myStub.Stub(x => x.ListCount()) .

(aparté : je sais que vous pouvez utiliser stub.AssertWasCalled() pour réaliser à peu près la même chose que mock.Expect et avec une meilleure syntaxe de lecture, mais je ne fais qu'approfondir la différence entre mocks et stubs).

Roy Osherove a une très bonne explication des mocks et des stubs.

Veuillez poster plus de code !

Nous avons besoin d'une image complète de la façon dont vous créez le stub (ou mock) et comment le résultat est utilisé par rapport à la classe testée. Est-ce que ListCount a un paramètre d'entrée ? Si oui, que représente-t-il ? Cela vous importe-t-il si c'est appelé avec une certaine valeur ? Vous souciez-vous de savoir si ListCount renvoie à une certaine valeur ?

Comme Simon Laroche l'a souligné, si le gestionnaire ne fait rien avec la valeur de retour de ListCount, alors le test ne passera pas ou n'échouera pas à cause de cela. Tout ce que le test attend, c'est que la méthode simulée/stubée soit appelée - rien de plus.

Pour mieux comprendre le problème, considérez trois éléments d'information et vous comprendrez vite :

  1. Ce qui est testé
  2. Dans quelle situation ?
  3. Quel est le résultat attendu ?

Comparez : Tests basés sur l'interaction avec les mocks . L'appel sur le simulacre est le test.

[Test]
public void calling_ListCount_calls_ListCount_on_DAL()
{
   // Arrange
   var dalMock = MockRepository.Mock<IDAL>();
   var dalMock.Expect(x => x.ListCount()).Returns(1);
   var manager = new Manager(dalMock);

   // Act
   manager.ListCount();

   // Assert -- Test is 100% interaction based
   dalMock.VerifyAllExpectations();   
}

Test basé sur l'État avec un talon . Le talon pilote le test, mais ne fait pas partie de l'attente.

[Test]
public void calling_ListCount_returns_same_count_as_DAL()
{
   // Arrange
   var dalStub = MockRepository.Stub<IDAL>();
   var dalStub.Stub(x => x.ListCount()).Returns(1);
   var manager = new Manager(dalMock);

   // Act
   int listCount = manager.ListCount();

   // Assert -- Test is 100% state based
   Assert.That(listCount, Is.EqualTo(1),
       "count should've been identical to the one returned by the dal!");
}

Personnellement, je privilégie les tests basés sur l'état lorsque c'est possible, bien que les tests basés sur l'interaction soient souvent nécessaires pour les API conçues avec des fonctionnalités de type Dites-le, ne demandez pas à l'esprit, car vous n'aurez pas d'état exposé à vérifier !

Confusion d'API. Les Mocks ne sont pas des stubs. Ou bien le sont-ils ?

La distinction entre un mock et un stub dans les mocks rhino est confuse. Traditionnellement, les stubs ne sont pas censés avoir des attentes - donc si votre double de test n'avait pas sa méthode appelée, cela ne causerait pas directement l'échec du test.

... Cependant, l'API Rhino Mocks est puissante, mais déroutante car elle vous permet de définir des attentes sur les stubs, ce qui, pour moi, va à l'encontre de la terminologie acceptée. Je ne pense pas beaucoup à la terminologie non plus, remarquez. Il vaudrait mieux que la distinction soit éliminée et que les méthodes appelées sur le test double définissent le rôle, à mon avis.

0 votes

+1 de ma part. Je suis également nouveau dans Rhino Mocks. Merci d'avoir montré .Stub. J'avais utilisé .Expect parce que c'était ce que tous les exemples utilisaient...

0 votes

Je fais toujours des tests de comportement en utilisant des stubs, comme cela est fait dans le post d'Ayende : ayende.com/blog/3384/

1voto

Simon Laroche Points 402

Je pense que cela a à voir avec ce que votre manager.ListCount() fait avec la valeur de retour.

S'il ne l'utilise pas, votre DAL peut renvoyer n'importe quoi, cela n'aura aucune importance.

public class Manager
{
    public Manager(DAL data)
    { 
        this.data = data
    }
    public void ListCount()
    {
        data.ListCount(1); //Not doing anything with return value
        DoingSomeOtherStuff();
    }    
}

Si votre compteur de liste fait quelque chose avec la valeur, vous devriez alors mettre des assertions sur ce qu'il fait. Par exemple

Assert.IsTrue(manager.SomeState == "someValue");

0 votes

+1 pour la distinction entre l'attente du double test et le test de l'Etat

0voto

murki Points 317

Avez-vous essayé d'utiliser

data.AssertWasCalled(x => x.ListCount(1) = Arg.Is(EXPECTED_VALUE));

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