Sinon: スパイ `returnValue`はジェネレーターでは機能しません

作成日 2015年01月03日  ·  20コメント  ·  ソース: sinonjs/sinon

ジェネレーター関数を使用する場合、スパイの戻り値は常にundefinedです。 失敗したテストケースは次のとおりです。

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

npm install co mocha should sinon && ./node_modules/.bin/mocha --harmony index.jsで実行します。

2.x Unverified

全てのコメント20件

SinonはまだES6機能をサポートしていません。 これに関する作業はまだ始まっていないので、いつこれが機能するかわかりません。

@mantoni 、そうですね、これは機能リクエストのようなものでした:)たとえば、ある種のタグ( "es6"?)を付けて開いたままにして、ロードマップに追加したときに簡単に再作成できるようにしますか? -何をする必要があるかを評価しますか?

これは素晴らしい機能です。

@ruimarinho私はこれを見つけましたhttps://github.com/ingameio/SinonES6.JS
require( "sinon")をrequire( "sinon-es6")に置き換えると、テストに合格するようです

-
2017年6月22日に@ fatso83によって編集:
これは真実ではないようです。 私はこれを手動でテストしました。 それはすべて同じように失敗します。これは、 sinon-es6 _がモック用のジェネレーターのサポートのみを実装しているためです。

誰かがこれに取り組んでいますか?

@emgeee私が知っていることではありません。

+1 @ruimarinho sinon.jsにes6機能を追加すると、すばらしいでしょう。

@ingameioはあなたの変更でPRをする気になりますか?

@ fatso83提案をありがとう!
すぐにPRします;)

パッケージ化されたバージョンに対してバスターテストを実行すると問題が発生します。 デバッグをしましたが、今日は時間が足りないので問題を解決できませんでした。
誰かが私が私のフォークに変更をプッシュするのを手伝いたいなら、私に言ってください、そして私はそれをします。

@gaguirreそれは素晴らしいニュースです。 MaximillianはすべてのテストをMochaに変換し、テストのセットアップにいくつかの変更を加えたので、 masterから最新の変更を取得するとうまくいくかもしれません。

@gaguirre誰かがこのスレッドに遭遇した場合に備えて、通行人がそれを見ることができるように、とにかく変更をフォークにプッシュすることはアイデアかもしれないと思います。

@ fatso83最初のバージョンunderscope / sinonにプッシュしました。
主な問題は、es6(この場合はphantomjs)をサポートしないエンジンでテストを実行するときに発生します。コードの解析時に失敗するため、try /で囲むことができるため、回避策としてeval()を使用しています。キャッチ。 これは受け入れられないと思いますが、それは出発点です。

現在、ジェネレーター呼び出しを別のファイルにラップして動的に要求しようとしていますが、動作していません。browserifyは、動的に必要なファイルもバンドルしているためです。 npm run test-headlessを実行しているときに、一部のファイルを除外できるかどうか疑問に思っています。

何が最善の解決策になると思いますか?

フィードバックをありがとう、 @ gaguirre 。 したがって、基本的な問題は、エンジンが解析をサポートしていない場合に解析を回避する方法が必要であるということです。 これは、WebWorkersなどで行うような単純な機能検出よりも少し難しいです。

実際のチェックを委任するだけでif(require('generator-support')){ ... }と同じくらい簡単ですが、これで実際の解析の問題が修正されるわけではありません。

@mantoniと@mroderick、これにきれいに対処する方法について何かアイデアはありますか?

PS私はオフラインで休みます-数時間の休日なので、イースターの後しばらく経つまで答えを読むことができません。

このレベルのジェネレータスタブが実際に機能するかどうかはわかりません(特にコルーチン指向のシナリオで)-非同期動作のエミュレーションが適切である可能性があります。 しかし、構文解析の問題を回避する方法があると思います。

提案

ジェネレーターコードを別のファイルに抽出し、ジェネレーター関数をラップするときにオンデマンドでのみそのファイルをrequire()します。

?/es6-support.js:

"use strict";

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

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

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

(単体テストでも同様のことを行い、コードカバレッジの追跡によって「es6-support」ファイルが自動的に含まれないようにします。)

代替案

sinon 2.0のリリースの準備が整うまでに、ES5の互換性はまだ価値のある目標になるのでしょうか。 おそらく、トランスパイラーのユーザーがいなくてもレガシーES5のみの環境をサポートしている少数の人々に、1.xに取り残されることを伝えるときが来たのでしょう。

@ evan-kingこの条件付きrequireは、ブラウザービルドで機能しなくなるため、解決策ではありません。 条件付きの要件は依存関係グラフの静的分析を無効にするため、WebPack、Rollup、Browserifyなどのツールはそれを処理できません。 完全にインまたは完全にアウトのいずれかです。

そして、はい、ES5の互換性は重要です。 ES6ジェネレーターのサポートはせいぜい見苦しいものであり、プロジェクトにSinonを使用するために追加のツールを使用するように人々に強いることは、テストを行うための基準を引き上げます。 そして多くの人にとって、そのしきい値はそれなりに十分に高いものです。 スクリプトタグを簡単に含めることができるのは、私見の重要な機能です。 ES5との互換性がいつか破られる可能性がありますが、ロードマップには含まれておらず、Sinon3についても議論されていません。Sinon2は事実上かなり長い間リリースされています。 公式リリースを行うのを妨げるいくつかの小さな迷惑があります。

私が思いつくことができる既存のES5ランタイム互換性を壊さずにES6互換性を取得する基本的に2つの方法があります、そして私は最後のものについて本当によくわかりません(それをテストしていません):

モジュールの条件付き評価

これはハックですが、かなり簡単です。 つまり、静的アセットのインライン化とES6機能のテストを利用して、必要なモジュールを評価するかどうかを決定できます。 これにより、ES5の互換性が保証され(ランタイムが構文をサポートしている場合にのみSinonにパッチが適用されます)、ライブラリメーカー側とクライアントユーザーのどちらにもBabelの形式で追加のツールを追加する必要はなく、ES6をテストするとES5ブラウザーが破損します。いずれにせよ失敗したでしょう。

brfsのようなものが配置されていると仮定すると、コードは次のようになります。

_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"); }
}

バベル化シノン

これは私が実際に試したことがないので私が確信していないものです。 Babelを介してビルドをES5にトランスパイルすると、ES6コードをあちこちに記述でき、上記のようなフープを飛び越えないようにすることができます。それでも、ジェネレーターに対して同じ種類のチェックを使用できます。 これらは、ES5コンストラクトを使用して実装されます。 もちろん、ES5ブラウザでのES6のテストは失敗します。 これはクライアント側の前のものと同じ利点がありますが、 yieldasyncfunction*()などのES2015の知識が到達するにはほど遠いため、貢献を妨げる可能性があります幅広い聴衆。

+1

@rpavlovs 、スレッドに+1を追加しても意味がありません。 GitHub UIには、感情を表現する必要がある場合に、各コメントの上に「リアクションを追加」ボタンがあります。 +1は何もしません。 一方、上記の提案の1つ(またはより賢いもの)を実装するプルリクエストは、この問題を修正する可能性がはるかに高くなります😄

ジェネレーターのAPIサポートがどのように見えるかについての入力をお願いします。これに数時間を使用した後でも、人々が何を望んでいるかはまだよくわかりません。

これを具体化するために、 @ ingameioのモックAPIへの変更を保持すると同時に、ES5の互換性を壊さない(上記のハックを使用)新しいブランチを作成しました。

少しイライラするのは、Ingameioによる元の変更をテストする方法が本当にわからないことです。これは、テスト例がどれも機能しないためです。フォーク内の例でさえ完全ではなく、事前/投稿を壊すものを取得できません。変更します。

ジェネレーターは単純なものです。過去を覚えている穏やかな同期の存在です。 したがって、 coやその他の無関係なもので例をなぞなぞしないでください。何が必要で、何が機能しないのかがわかりにくくなります。 たとえば、上の例は非常に複雑であり、「yield式」の戻り値が「yieldvalue」と同じであると想定しているため、 yieldの動作も間違っているようです。 歩留まりのresultは、ジェネレーターのnext()MDN )に渡される値です。

coを使用した例は、おそらくMochaテストで直接yieldを使用できるようにするためにのみ使用していることを認識していますが、IIFEまたは他の方法で例をラップするだけです。より明確にするために。

これは、ジェネレーターがどのようにサポートするか(今日の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();
    });
});

では、どのAPI拡張機能が必要ですか?
元のテストから、私たちは次のようなものを見たいと思います

foo.biz.firstGeneratedValue.should.equal('biz');
また
foo.biz.generatedValue[0].should.equal('biz');

cc @ruimarinho

元のテストにエラーがあり、Sinonでのジェネレーターの処理に問題がないため、この問題を解決します。

ジェネレーター(およびそれに関連するイテレーター)を処理するためのAPIが問題#1467でどのように見えるかについてのディスカッションに参加してください

このページは役に立ちましたか?
0 / 5 - 0 評価