Sinon: withArgs funktioniert nicht richtig mit sinon.mock

Erstellt am 30. Aug. 2017  ·  3Kommentare  ·  Quelle: sinonjs/sinon

  • Sinon-Version: 3.2.1
  • Umgebung: OSX, Knoten 7
  • Andere Bibliotheken, die Sie verwenden: Mocha, mjackson/expect

Was haben Sie erwartet?
Wenn ich einen Mock für eine Klasse erstelle und versuche, das Verhalten einer ihrer Funktionen mit withArgs zu steuern, wird eine Ausnahme ausgelöst. Tritt nur auf, wenn mehrere Aufrufe von withArgs .

Was passiert eigentlich

So reproduzieren Sie


Reproduzierbares Beispiel

Angenommen, ich habe eine Klasse C deklariert:

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

Ich versuche, das Verhalten von foo mit einem Mock auf C zu kontrollieren:

      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();
      });

Was die folgende Ausnahme auslöst:

ExpectationError: foo hat falsche Argumente erhalten ["a", "b"], erwartet ["c", "d"]
at Object.fail (node_modules/sinon/lib/sinon/mock-expectation.js:281:25)
bei Funktion.(node_modules/sinon/lib/sinon/mock-expectation.js:182:33)
bei Array.forEach (nativ)
at Function.verifyCallAllowed (node_modules/sinon/lib/sinon/mock-expectation.js:175:27)
at Function.invoke (node_modules/sinon/lib/sinon/mock-expectation.js:78:14)
beim Proxy (node_modules/sinon/lib/sinon/spy.js:89:22)

Während dieser Code funktioniert:

      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();
      });

Es ist erwähnenswert, dass die folgenden Szenarien wie erwartet funktionieren:

      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();
      });

Das heißt, das Problem tritt nur auf, wenn withArgs mehrmals verwendet wird.
Es könnte mit #1381 verwandt sein, aber wie Sie im Beispiel sehen können, funktioniert die dort vorgeschlagene Lösung nicht.

Bug Medium Help wanted stale

Hilfreichster Kommentar

Ich habe mir den Code angeschaut.
Zunächst funktioniert dieser Test:

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();
});

Es gibt zwei Gründe, warum dies funktioniert und der Test in der ursprünglichen Ausgabe nicht:

  • Ich erstelle zwei expects .
  • Ich rufe die statische Methode anstelle der von expects .

Das heißt, das funktioniert auch nicht:

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();
});

Warum passiert das?

Jedes Mal, wenn expects aufgerufen wird, passiert Folgendes:

  • Eine neue Erwartung (mit minCalls/maxCalls erweiterter Stub - das sind die Variablen, die beispielsweise geändert werden, wenn Sie once() aufrufen) wird erstellt, nur dass es sich um einen "Scoped-Stub" handelt Verbindung zu anderen expects . Es ist das von expects Objekt.
  • Es wird der Liste der Erwartungen hinzugefügt.

Dies führt zu unterschiedlichen Verhaltensweisen für diese Szenarien:

  • Wenn Sie die von expects Methode aufrufen, wird sie nur auf diese expects .
  • Wenn Sie die Methode direkt aufrufen und nicht den Stub, durchsucht sie alle expects und findet eine passende. Wenn es mehr als einen gibt, wird der erste aufgerufen.

Ist das das richtige Verhalten? Es scheint mir intuitiver, dass, sobald jemand expects für eine Methode aufruft, diese sich genau wie ein stub verhält (mit den zusätzlichen minCalls/maxCalls ). Dies führt zu:

  • Mehr Code kann zwischen stub und mock-expectation .
  • Das Verhalten der obigen Szenarien ist das gleiche.

Was würde nun mit minCalls/maxCalls passieren?

Da es jetzt nur noch ein expectation , schlage ich drei alternative Möglichkeiten vor, die Anrufanzahl mit Mocks zu überprüfen:

  • Erlaube, once() , twice() usw. zu verketten.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

Könnte aber etwas verwirrend sein.

  • Erlauben Sie nicht, once() , twice() usw. zu verketten.
  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();

Klarer, aber Sie können foo mehr als einmal stoppen, also brauchen wir einen Weg, dies zu umgehen. Auch dies ist weiterhin möglich:

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • Verwenden Sie die Spy-API, die sich die Anzahl der Anrufe merkt.

Alle 3 Kommentare

Danke für einen beispielhaften Problembericht . Ich benutze die Mocks-Funktionalität nicht, da sie mein Gehirn verletzt, also muss sich jemand anderes das ansehen, aber Ihre Beispiele sollten das viel einfacher machen.

Schauen Sie sich den Code gerne selbst an - oft der schnellste Weg, dies zu beheben :-)

Ich habe mir den Code angeschaut.
Zunächst funktioniert dieser Test:

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();
});

Es gibt zwei Gründe, warum dies funktioniert und der Test in der ursprünglichen Ausgabe nicht:

  • Ich erstelle zwei expects .
  • Ich rufe die statische Methode anstelle der von expects .

Das heißt, das funktioniert auch nicht:

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();
});

Warum passiert das?

Jedes Mal, wenn expects aufgerufen wird, passiert Folgendes:

  • Eine neue Erwartung (mit minCalls/maxCalls erweiterter Stub - das sind die Variablen, die beispielsweise geändert werden, wenn Sie once() aufrufen) wird erstellt, nur dass es sich um einen "Scoped-Stub" handelt Verbindung zu anderen expects . Es ist das von expects Objekt.
  • Es wird der Liste der Erwartungen hinzugefügt.

Dies führt zu unterschiedlichen Verhaltensweisen für diese Szenarien:

  • Wenn Sie die von expects Methode aufrufen, wird sie nur auf diese expects .
  • Wenn Sie die Methode direkt aufrufen und nicht den Stub, durchsucht sie alle expects und findet eine passende. Wenn es mehr als einen gibt, wird der erste aufgerufen.

Ist das das richtige Verhalten? Es scheint mir intuitiver, dass, sobald jemand expects für eine Methode aufruft, diese sich genau wie ein stub verhält (mit den zusätzlichen minCalls/maxCalls ). Dies führt zu:

  • Mehr Code kann zwischen stub und mock-expectation .
  • Das Verhalten der obigen Szenarien ist das gleiche.

Was würde nun mit minCalls/maxCalls passieren?

Da es jetzt nur noch ein expectation , schlage ich drei alternative Möglichkeiten vor, die Anrufanzahl mit Mocks zu überprüfen:

  • Erlaube, once() , twice() usw. zu verketten.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

Könnte aber etwas verwirrend sein.

  • Erlauben Sie nicht, once() , twice() usw. zu verketten.
  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();

Klarer, aber Sie können foo mehr als einmal stoppen, also brauchen wir einen Weg, dies zu umgehen. Auch dies ist weiterhin möglich:

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • Verwenden Sie die Spy-API, die sich die Anzahl der Anrufe merkt.

Dieses Problem wurde automatisch als veraltet markiert, da es in letzter Zeit keine Aktivität hatte. Es wird geschlossen, wenn keine weitere Aktivität stattfindet. Vielen Dank für Ihre Beiträge.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

fearphage picture fearphage  ·  3Kommentare

ALeschinsky picture ALeschinsky  ·  4Kommentare

NathanHazout picture NathanHazout  ·  3Kommentare

stephanwlee picture stephanwlee  ·  3Kommentare

stevenmusumeche picture stevenmusumeche  ·  3Kommentare