Sinon: Spy `returnValue` funktioniert nicht mit Generatoren

Erstellt am 3. Jan. 2015  ·  20Kommentare  ·  Quelle: sinonjs/sinon

Beim Arbeiten mit Generatorfunktionen ist der Rückgabewert eines Spions immer undefined . Hier ist ein fehlgeschlagener Testfall:

require('should');

var sinon = require('sinon');
var co = require('co');

var foo = {
  bar: function() {
    return 'bar';
  },
  biz: function *() {
    return 'biz';
  }
}

describe('Return value', function() {
  it('should work with regular functions', function() {
    sinon.spy(foo, 'bar');

    foo.bar();

    foo.bar.firstCall.returnValue.should.equal('bar');
  });

  it('should work with generator functions', co.wrap(function*() {
    sinon.spy(foo, 'biz');

    var result = yield foo.biz();

    result.should.equal('biz');
    foo.biz.firstCall.returnValue.should.equal('biz');
  }));
});

Führen Sie mit npm install co mocha should sinon && ./node_modules/.bin/mocha --harmony index.js .

2.x Unverified

Alle 20 Kommentare

Sinon unterstützt noch keine ES6-Funktionen. Die Arbeit daran hat noch nicht begonnen, daher weiß ich nicht, wann dies funktionieren wird.

@mantoni , richtig, das war eher eine Funktionsanfrage :) Möchten Sie es zum Beispiel mit einer Art Tag ("es6"?) Offen lassen, damit es einfacher ist, es erneut zu erstellen, wenn es zur Roadmap hinzugefügt wird -auswerten, was getan werden muss?

Dies wäre ein großartiges Feature.

@ruimarinho Ich habe dies gefunden https://github.com/ingameio/SinonES6.JS
Ersetzen von require("sinon") durch require("sinon-es6") scheint Ihre Tests zu bestehen

--
bearbeitet von @fatso83 am 22. Juni 2017:
Dies scheint nicht zu stimmen. Ich habe das manuell getestet. Es schlägt trotzdem fehl, was absolut sinnvoll ist, da sinon-es6 _nur seine Unterstützung für Generatoren zum Spotten von _ implementiert.

Arbeitet jemand daran?

@emgeee nicht, dass ich wüsste.

+1 @ruimarinho sinon.js mit es6-Funktionen wäre großartig!

@ingameio möchtest du eine PR mit deinen Änderungen machen?

@fatso83 danke für den Vorschlag!
PR mache ich bald ;)

Ich habe Probleme beim Ausführen von buster-test gegen die gepackte Version. Ich habe etwas debuggt, aber ich habe heutzutage wenig Zeit, sodass ich das Problem nicht lösen konnte.
Wenn jemand helfen möchte, könnte ich die Änderungen auf meinen Fork schieben, sag es mir einfach und ich werde es tun.

@gaguirre Das sind großartige Neuigkeiten. Maximillian hat gerade alle Tests auf Mocha umgestellt und dabei einige Änderungen am Testaufbau vorgenommen, also klappt es vielleicht, wenn Sie die neuesten Änderungen von master ?

@gaguirre Falls jemand über diesen Thread stolpern sollte, könnte es meiner Meinung nach eine Idee sein, die Änderungen trotzdem auf deinen Fork zu schieben, damit ein Passant sie sehen kann.

@fatso83 Ich habe eine erste Version nach underscope/sinon geschoben.
Das Hauptproblem tritt auf, wenn die Tests mit einer Engine ausgeführt werden, die es6 nicht unterstützt (in diesem Fall Phantomjs): Es schlägt beim Analysieren des Codes fehl, daher verwende ich eval() als Workaround, da es von einem try/ umgeben sein kann Fang. Ich denke, das ist nicht akzeptabel, aber es ist ein Ausgangspunkt.

Im Moment versuche ich, den Generatoraufruf in eine andere Datei zu packen und dynamisch anzufordern, aber es funktioniert nicht, denke ich, weil browserify sogar die dynamisch erforderlichen Dateien bündelt. Ich frage mich, ob einige Dateien beim Ausführen npm run test-headless ausgeschlossen werden könnten.

Was könnte Ihrer Meinung nach die beste Lösung sein?

Danke für das Feedback, @gaguirre . Das grundlegende Problem ist also, dass wir eine Möglichkeit brauchen, das Parsen zu vermeiden, wenn die Engine dies nicht unterstützt. Das ist etwas schwieriger als die einfache Feature-Erkennung, wie wir es bei WebWorkers usw.

Nur die eigentliche Prüfung zu delegieren ist so einfach wie if(require('generator-support')){ ... } , aber das eigentliche Parsing-Problem wird dadurch nicht behoben.

Irgendwelche Ideen, wie man sauber damit umgehen kann, @mantoni und @mroderick?

PS Ich bin in ein paar Stunden im Offline-Urlaub, kann die Antworten also erst einige Zeit nach Ostern lesen.

Ich kann nicht sagen, ob diese Ebene des Generator-Stubbing tatsächlich funktioniert (insbesondere in Coroutine-orientierten Szenarien) - es scheint, dass eine gewisse Emulation des asynchronen Verhaltens angebracht sein könnte. Aber ich denke, es gibt einen Weg, um das Parsing-Problem zu umgehen.

Vorschlag :

Extrahieren Sie den Generatorcode in eine separate Datei und require() diese Datei nur bei Bedarf, wenn Sie eine Generatorfunktion umschließen, dh:

?/es6-support.js:

"use strict";

exports.getGeneratorWrapper = function(method, ctx, args) {
    return function* () {
        return mockObject.invokeMethod(method, ctx, args);
    }
}

lib/sinon/mock.js->erwartet...:

var wrapper = function () {
    return mockObject.invokeMethod(method, this, arguments);
});

if (/^function\s*\*/.test(method.toString())) {
    wrapper = require('es6-support').getGeneratorWrapper(method, this, arguments);
}

wrapMethod(this.object, method, wrapper);

(Gehen Sie ähnlich mit den Komponententests vor und stellen Sie sicher, dass die Verfolgung der Codeabdeckung nicht dazu führt, dass die Datei „es6-support“ automatisch eingeschlossen wird.)

Alternativvorschlag :

Wird die ES5-Kompatibilität bis zur Veröffentlichung von Sinon 2.0 immer noch ein lohnendes Ziel sein? Vielleicht ist es an der Zeit, den wenigen Leuten, die immer noch Legacy-ES5-Umgebungen ohne den Benutzer von Transpilern unterstützen, zu sagen, dass sie auf 1.x zurückbleiben werden.

@evan-king Diese bedingte Anforderung ist keine Lösung, da sie in unseren Browser-Builds bricht. Da die Bedingung die statische Analyse des Abhängigkeitsdiagramms besiegt, können Tools wie WebPack, Rollup und Browserify damit nicht umgehen. Es ist entweder total in oder total out.

Und ja, ES5-Kompatibilität ist eine Sache. Der ES6-Generator-Support ist bestenfalls dürftig, und wenn man Leute dazu zwingt, zusätzliche Tools zu verwenden, um Sinon für ihre Projekte zu verwenden, wird die Messlatte für das Testen höher gelegt. Und für viele ist diese Schwelle ohnehin schon hoch genug. In der Lage zu sein, einfach ein Skript-Tag einzufügen, damit es funktioniert, ist meiner Meinung nach ein wichtiges Feature. Wir könnten irgendwann die ES5-Kompatibilität brechen, aber es ist nicht auf unserer Roadmap und wurde nicht einmal für Sinon 3 diskutiert. Sinon 2 ist tatsächlich seit geraumer Zeit auf dem Markt; Es gibt nur ein paar kleinere Ärgernisse, die uns von einer offiziellen Veröffentlichung abhalten.

Es gibt grundsätzlich zwei Möglichkeiten, ES6-Kompatibilität zu erreichen, ohne die bestehende ES5-Laufzeitkompatibilität zu beeinträchtigen, die ich mir ausdenken kann, und ich bin mir bei der letzten nicht wirklich sicher (habe sie nicht getestet):

Bedingte Bewertung von Modulen

Dies ist ein Hack, aber ziemlich einfach. Das bedeutet, dass wir uns auf statisches Asset-Inlining und ES6-Funktionstests verlassen können, um zu entscheiden, ob wir das erforderliche Modul evaluieren. Dadurch wird die ES5-Kompatibilität sichergestellt (Sinon wird nur gepatcht, wenn die Laufzeitumgebung die Syntax unterstützt), es besteht keine Notwendigkeit für zusätzliche Tools in Form von Babel, weder auf der Seite des Bibliotheksherstellers noch auf der Seite des Client-Benutzers, und das Testen von ES6 wird die ES5-Browser beschädigen wäre in jedem Fall gescheitert.

Unter der Annahme, dass so etwas wie brfs vorhanden ist, würde der Code in etwa so aussehen:

_runtime-features.js_

var features = {};
try { 
    new Function("var foo = function* foo(){ }") ;
    features.generatorSupport = true; 
} 
catch(err){ features.generatorSupport = false; }
module.exports = features;

_es6-generator-wrapper.js_

return function* () {
    return mockObject.invokeMethod(method, ctx, args);
}

_es6.js_

var features = require('./runtime-features');

if( features.generatorSupport ) {
    var code = fs.readFileSync('./es6-generator-wrapper.js');
    module.exports.getGeneratorWrapper = new Function("mockObject", "method", "ctx", "args", code);
} else {
    module.exports.getGeneratorWrapper = function() { throw new TypeError("You tried using a generator function in a runtime only capable of running ES5 code"); }
}

Babelysierende Sinon

Bei diesem bin ich mir nicht sicher, da ich es nicht wirklich versucht habe. Wenn wir den Build von Babel nach ES5 transpilieren, können wir überall ES6-Code schreiben, vermeiden, durch Reifen zu springen, wie ich es oben getan habe, und wir können immer noch die gleichen Arten von Prüfungen für Generatoren verwenden. Sie werden nur mit ES5-Konstrukten implementiert. Natürlich wird das Testen von ES6 in ES5-Browsern immer noch fehlschlagen. Dies hat den gleichen Vorteil wie das vorherige auf der Clientseite, aber wir könnten Beiträge behindern, da ES2015-Wissen zu Sachen wie yield , async , function*() weit davon entfernt ist ein breites Publikum.

+1

@rpavlovs , es macht wirklich keinen Sinn, dem Thread +1 hinzuzufügen. Die GitHub-Benutzeroberfläche verfügt über eine Schaltfläche „Reaktion hinzufügen“ über jedem Kommentar, wenn Sie Ihre Gefühle ausdrücken möchten. Ein +1 wird nichts tun. Ein Pull-Request, der einen der obigen Vorschläge (oder etwas Clevereres) umsetzt, hat dagegen eine weitaus bessere Chance, dieses Problem zu beheben 😄

Ich hätte gerne Informationen darüber, wie die API-Unterstützung für Generatoren aussehen würde , da ich nach ein paar Stunden immer noch nicht sicher bin, was die Leute gerne sehen würden.

Um dies zu konkretisieren, habe ich einen neuen Zweig erstellt, der die Änderungen von @ingameio an der Schein-API enthält, während er gleichzeitig die ES5-Kompatibilität nicht beeinträchtigt (unter Verwendung des oben erwähnten Hacks).

Was mich ein bisschen ärgert, ist, dass ich wirklich nicht sagen kann, wie ich die ursprünglichen Änderungen von Ingameio testen soll, da keines der Testbeispiele funktioniert - nicht einmal das Beispiel in der Abzweigung ist vollständig, und ich kann nichts bekommen, um Pre/Post zu brechen Änderungen.

Generatoren sind einfache Dinge: sanfte synchrone Wesen, die sich an ihre Vergangenheit erinnern. Also verwirren Sie bitte keine Beispiele mit co und anderen Dingen, die nichts miteinander zu tun haben, da dies es schwieriger macht, zu erkennen, was gewollt ist/nicht funktioniert. Das obere Beispiel zum Beispiel ist ziemlich kompliziert und scheint auch falsch zu sein, was yield tut, da es erwartet, dass der Rückgabewert des "Ertragsausdrucks" derselbe wie der "Ergebniswert" ist. Die result des Ertrags ist der Wert, der an die next() ( MDN ) des Generators übergeben wird.

Mir ist klar, dass die Beispiele mit co es wahrscheinlich nur verwenden, um yield direkt im Mocha-Test verwenden zu können, aber packen Sie Ihr Beispiel einfach in ein IIFE oder eine andere Möglichkeit, dasselbe zu erreichen der mehr klarheit halber.

Dies ist ein einfacher Test, wie Generatoren unterstützen (funktioniert im heutigen Sinon):

require("should");

var sinon = require("../sinon");

var foo = {
    bar: function () {
        return "bar";
    },
    biz: function *() {
        return "biz";
    }
};

describe("generator support", function () {
    it('should work with generator functions',  function(){
        var spy = sinon.spy(foo, 'biz');

        var iterator = foo.biz();
        var result = iterator.next();

        result.value.should.equal('biz');
        result.done.should.equal(true);
        spy.firstCall.returnValue.should.be.an.Object();
        spy.firstCall.returnValue.next.should.be.a.Function();
    });
});

Welche API-Erweiterungen würden wir uns wünschen?
Aus dem ursprünglichen Test gehe ich davon aus, dass wir so etwas gerne sehen würden

foo.biz.firstGeneratedValue.should.equal('biz');
oder
foo.biz.generatedValue[0].should.equal('biz');
?

cc @ruimarinho

Ich schließe dieses Thema, da der ursprüngliche Test einen Fehler hatte und ich keine Probleme mit der Handhabung des Generators in Sinon erkennen kann.

Bitte nehmen Sie an der Diskussion darüber teil, wie eine API für den Umgang mit Generatoren (und den zugehörigen Iteratoren) in Ausgabe #1467 aussehen würde

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

stevenmusumeche picture stevenmusumeche  ·  3Kommentare

stephanwlee picture stephanwlee  ·  3Kommentare

kbirger picture kbirger  ·  3Kommentare

NathanHazout picture NathanHazout  ·  3Kommentare

kevinburkeshyp picture kevinburkeshyp  ·  4Kommentare