Sinon: withArgs ne fonctionne pas correctement avec sinon.mock

Créé le 30 août 2017  ·  3Commentaires  ·  Source: sinonjs/sinon

  • Sinon version : 3.2.1
  • Environnement : OSX, nœud 7
  • Autres bibliothèques que vous utilisez : Mocha, mjackson/expect

Que vous attendiez-vous à ce qu'il se passe?
Lorsque je crée une simulation sur une classe et que j'essaie de contrôler le comportement de l'une de ses fonctions à l'aide de withArgs , une exception est levée. Se produit uniquement lors de l'utilisation de plusieurs appels de withArgs .

Que se passe-t-il réellement

Comment reproduire


Exemple reproductible

Supposons que j'ai déclaré une classe C :

      class C {
        static foo(arg1, arg2) {return 'foo';}
      }

J'essaie de contrôler le comportement de foo utilisant un simulacre sur C :

      it('test withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);
        fooStub.withArgs('c', 'd').returns(2);

        expect(fooStub('a', 'b')).toEqual(1);
        expect(fooStub('c', 'd')).toEqual(2);

        CMock.restore();
      });

Ce qui lève l'exception suivante :

ExpectationError : foo a reçu des arguments erronés ["a", "b"], attendu ["c", "d"]
à Object.fail (node_modules/sinon/lib/sinon/mock-expectation.js:281:25)
à Fonction.(node_modules/sinon/lib/sinon/mock-expectation.js:182:33)
à Array.forEach (natif)
à Function.verifyCallAllowed (node_modules/sinon/lib/sinon/mock-expectation.js:175:27)
à Function.invoke (node_modules/sinon/lib/sinon/mock-expectation.js:78:14)
au proxy (node_modules/sinon/lib/sinon/spy.js:89:22)

Pendant que ce code fonctionne :

      it('test withArgs stub', () => {
        let fooStub = sinon.stub(C, 'foo');

        fooStub.withArgs('a', 'b').returns(1);
        fooStub.withArgs('c', 'd').returns(2);

        expect(fooStub('a', 'b')).toEqual(1);
        expect(fooStub('c', 'd')).toEqual(2);

        fooStub.restore();
      });

Il convient de mentionner que les scénarios suivants fonctionnent comme prévu :

      it('test one withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);

        expect(fooStub('a', 'b')).toEqual(1); // ok

        CMock.restore();
      });
      it('test one withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);

        expect(fooStub('c', 'd')).toEqual(1); // error: foo received wrong arguments ["c", "d"], expected ["a", "b"]

        CMock.restore();
      });

Cela signifie que le problème se produit uniquement lors de l'utilisation de withArgs plusieurs fois.
Cela peut être lié à #1381, mais comme vous pouvez le voir dans l'exemple, la solution suggérée ne fonctionne pas.

Bug Medium Help wanted stale

Commentaire le plus utile

J'ai regardé le code.
Tout d'abord, ce test fonctionne :

class C {
  static foo(arg1, arg2) {return 'foo';}
}

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(C.foo('a', 'b')).toEqual(1);
  expect(C.foo('c', 'd')).toEqual(2);

  CMock.restore();
});

Il y a deux raisons pour lesquelles cela fonctionne et le test du numéro d'origine ne fonctionne pas :

  • Je crée deux expects .
  • J'appelle la méthode statique au lieu de celle renvoyée par expects .

Cela signifie que cela ne fonctionne pas non plus :

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  let fooStub = CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(fooStub('a', 'b')).toEqual(1); // Error: foo received wrong arguments ["a", "b"], expected ["c", "d"]
  expect(fooStub('c', 'd')).toEqual(2);

  CMock.restore();
});

Pourquoi cela arrive-t-il ?

Chaque fois que expects est appelé, ce qui suit se produit :

  • Une nouvelle attente (stub améliorée avec minCalls/maxCalls - qui sont les variables qui sont modifiées lorsque vous appelez once() par exemple) est créée, seulement qu'il s'agit d'un "stub étendu", il avoir une connexion avec d'autres expects . C'est l'objet renvoyé par expects .
  • Il est ajouté à la liste des attentes.

Ce qui se traduit par des comportements différents pour ces scénarios :

  • Si vous appelez la méthode renvoyée par expects , elle sera limitée uniquement à ce expects .
  • Si vous appelez la méthode directement et non le stub, il recherchera dans tous les expects et en trouvera un qui convient. S'il y en a plusieurs, il appellera le premier.

Est-ce le comportement correct ? Il me semble plus intuitif qu'une fois que quelqu'un appelle expects sur une méthode, elle se comportera exactement comme un stub (avec le minCalls/maxCalls supplémentaire). Cela se traduira par :

  • Plus de code peut être partagé entre stub et mock-expectation .
  • Le comportement des scénarios ci-dessus sera le même.

Maintenant, qu'arriverait-il à minCalls/maxCalls ?

Puisqu'il n'y a qu'un seul expectation maintenant, je propose trois façons alternatives de vérifier le nombre d'appels avec des simulations :

  • Permet de concaténer once() , twice() etc.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

Peut-être un peu déroutant cependant.

  • Ne permettez pas de concaténer once() , twice() etc.
  CMock.expects('foo').withArgs('a', 'b').returns(1).twice(); // `twice()` returns `null`, so that one has to call `expects()` again to set expectations for other arguments. 
  CMock.expects('foo').withArgs('c', 'd').returns(2).once();

Plus clair, mais vous ne pouvez pas stub foo plus d'une fois, nous aurons donc besoin d'un moyen de contourner cela. De plus, cela reste possible :

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • Utilisez l'API Spy qui mémorise le nombre d'appels.

Tous les 3 commentaires

Merci pour un exemple de rapport de problème. Je n'utilise pas la fonctionnalité de simulation, car cela me fait mal au cerveau, donc quelqu'un d'autre devra y jeter un œil, mais vos exemples devraient rendre cela beaucoup plus facile.

N'hésitez pas à jeter un œil au code vous-même - souvent le moyen le plus rapide de résoudre ce problème :-)

J'ai regardé le code.
Tout d'abord, ce test fonctionne :

class C {
  static foo(arg1, arg2) {return 'foo';}
}

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(C.foo('a', 'b')).toEqual(1);
  expect(C.foo('c', 'd')).toEqual(2);

  CMock.restore();
});

Il y a deux raisons pour lesquelles cela fonctionne et le test du numéro d'origine ne fonctionne pas :

  • Je crée deux expects .
  • J'appelle la méthode statique au lieu de celle renvoyée par expects .

Cela signifie que cela ne fonctionne pas non plus :

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  let fooStub = CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(fooStub('a', 'b')).toEqual(1); // Error: foo received wrong arguments ["a", "b"], expected ["c", "d"]
  expect(fooStub('c', 'd')).toEqual(2);

  CMock.restore();
});

Pourquoi cela arrive-t-il ?

Chaque fois que expects est appelé, ce qui suit se produit :

  • Une nouvelle attente (stub améliorée avec minCalls/maxCalls - qui sont les variables qui sont modifiées lorsque vous appelez once() par exemple) est créée, seulement qu'il s'agit d'un "stub étendu", il avoir une connexion avec d'autres expects . C'est l'objet renvoyé par expects .
  • Il est ajouté à la liste des attentes.

Ce qui se traduit par des comportements différents pour ces scénarios :

  • Si vous appelez la méthode renvoyée par expects , elle sera limitée uniquement à ce expects .
  • Si vous appelez la méthode directement et non le stub, il recherchera dans tous les expects et en trouvera un qui convient. S'il y en a plusieurs, il appellera le premier.

Est-ce le comportement correct ? Il me semble plus intuitif qu'une fois que quelqu'un appelle expects sur une méthode, elle se comportera exactement comme un stub (avec le minCalls/maxCalls supplémentaire). Cela se traduira par :

  • Plus de code peut être partagé entre stub et mock-expectation .
  • Le comportement des scénarios ci-dessus sera le même.

Maintenant, qu'arriverait-il à minCalls/maxCalls ?

Puisqu'il n'y a qu'un seul expectation maintenant, je propose trois façons alternatives de vérifier le nombre d'appels avec des simulations :

  • Permet de concaténer once() , twice() etc.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

Peut-être un peu déroutant cependant.

  • Ne permettez pas de concaténer once() , twice() etc.
  CMock.expects('foo').withArgs('a', 'b').returns(1).twice(); // `twice()` returns `null`, so that one has to call `expects()` again to set expectations for other arguments. 
  CMock.expects('foo').withArgs('c', 'd').returns(2).once();

Plus clair, mais vous ne pouvez pas stub foo plus d'une fois, nous aurons donc besoin d'un moyen de contourner cela. De plus, cela reste possible :

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • Utilisez l'API Spy qui mémorise le nombre d'appels.

Ce problème a été automatiquement marqué comme obsolète car il n'a pas eu d'activité récente. Il sera fermé si aucune autre activité ne se produit. Merci pour vos contributions.

Cette page vous a été utile?
0 / 5 - 0 notes