次のような世界が与えられます:
require('cucumber').defineSupportCode( ({setWorldConstructor:world}) => {
world(class {
constructor(p) { ... }
foo(bar) { return new Promise(.... ) }
})
})
私は書くことができるようになりたい:
When(/^I do the foo with (.*)$/i, (ctx, bar) => ctx.foo(bar) );
それ以外の
When(/^I do the foo with (.*)$/i, function(bar) {
return this.foo(bar)
});
これにより、すべての非同期操作で.bind(this)
をモンキー化することもできなくなります。または、必要に応じて、 var that = this
を使用してモンキー化することもできなくなります。
もう1つの利点(IMHO)-それは人々が世界の継承ツリーから抜け出し、JSのより良い力に向けてより良い方向に向けるのに役立ちます
引数として常にworld
を受け入れることを意味することに同意します。これは、APIの変更です。
モカのように--ui exports
と--ui tdd
と--ui bdd
あると思います-キューも同様です。
これは基本的に、ステップが世界で呼び出される/適用されるか、それとも最初の引数として受け入れるかを決定するプロジェクト変数です。
長期的には、これだけの価値はないと思います。 はい、他の方法では使用できない=>
を使用できる1行のステップに適しています。 私の経験では、このような手順はほとんどありませんでした。 ES6に、コンテキストを保持しない矢印関数が含まれていなかったのは驚きです。これは私にとって理想的です。
これにより、すべての非同期操作で
.bind(this)
をモンキー化することもできなくなります。または、必要に応じて、var that = this
を使用してモンキー化することもできなくなります。
これを回避するために、ステップ定義内で太い矢印を使用できませんか?
もう1つの利点(IMHO)-それは人々が世界の継承ツリーから抜け出し、JSのより良い力に向けてより良い方向に向けるのに役立ちます
これについてもう少し説明していただけますか/例を挙げてください
簡単に言うと、私は、コンテキストをクラスとして表現する方法を避けてきたプログラマーの学校に所属しています。 より多くの人々がOOPの暗い側面にさらされるほど、この学校は大きくなります-そして、矢印機能がコンテキストを保持することを
宗教的な議論に巻き込まれることなく、私は生きることを信じて生きることを信じていると言いますが、あなたはクラスを使いたいですか? 素晴らしい。 私に強制しないでください:-)
私の一般的な感覚は、それは小さな変化かもしれないということです、そしてあなたがそれが最善であると思う方法の一般的な方向性を私に教えてくれれば私はそれを喜んでします;-)
ステップを定義するときは、クラスではなく関数を定義するので、論理的にはthis
はその中に場所がありません。
これは、OOPパラダイムではなくFPパラダイムの状況だと思います。
@osher私たちは、彼がクラスを使用することを強制したくないことを疑いの@charlierudolph利益を与えることができると思います。 とは言うものの、具体的な利点がない限り、このパッケージを使用している場所でAPIの変更を統合(破壊)することを強制されるべきではありません。
@charlierudolph 「JSの優れた能力」とは@osherはFP、関数型プログラミングを意味すると思います。
私見では、この変更により、ステップ定義が少し単純で論理的になります。 基本的には次の行を削除するだけです: const thisWorldNotThisStep = this
すべての非同期関数に.bind(this)
を適用する必要はまったくありません。また、ステップ定義内で矢印関数を使用している限り、 const self = this
パターンを使用する必要はありません。
問題(これは大きな問題ではありません)は、ステップ定義内で、 this
が、直感に反するステップではなく、世界を参照していることです。
わかった。 しかし、誰もが関数型プログラマーであるわけではなく、重大な変更の導入を防ぐために、世界を最初のパラメーターとして渡すオプションを作成することについてどう思いますか?
defineSupportCode(({setWorldInjectionStrategy}) => {
setWorldInjectionStrategy('this') // default
setWorldInjectionStrategy('argument') // makes world be the first argument to steps and hooks
})
私は、世界/コンテキストを注入するという考えに完全に同意します。ステップのパラメーターがあります。 このプロジェクトから始めて、すべてのJSエコシステムは、ES6で許可されている新しい機能を完全に受け入れるように動いています。それは良いことです❤️
ExpressやKoaなどの他のツールを見ると、現在のリクエストコンテキストがthis
ANDとしてミドルウェアの最初のパラメーターとして設定されています(これはキュウリのステップに相当します)。 このソリューションにより、従来の機能の使用とES6矢印機能の使用が可能になります。
最初のパラメータとしてのコンテキストのもう1つの利点は、
@charlierudolphによって提案されたソリューションの場合、これは長期的には機能しないと思います。これにより、コミュニティが2つに分割されます。 すべてのキュウリの例がすべてのインストールで機能するわけではなく、ドキュメントは2つに複製されます。 派手ではありません。
重大な変更の心配についてはよくわかりません。v2はこの種の変更を導入するのに最適な時期です。 待機すると、別のメジャーを作成/待機する必要があります。
Before
とAfter
を変更してworld
を注入するのはどうですか?
defineSupportCode(function({After, Before, Given}) {
let world;
// Asynchronous Callback
Before(function (w, scenarioResult, callback) {
world = w;
callback();
});
After(function (world, scenarioResult, callback) {
callback();
});
Given('I access the world', () => {
assert(world); // Yay!
});
});
このようにして、変数内のworld
をbefore
ステップでキャプチャできます。 各ステップの定義を台無しにすることはありません。
次に、古い作業方法を維持しますが、 deprecated
メッセージを生成します。 または、そうしないでください。そのアプローチを使用して、自分が何をしているのかを知りたい人のために、オプションをそのままにしておいてください。
オンラインで見つかったスニペットの互換性についての議論は説得力のある議論だと思います。
--ui
フラグ、またはsetWorldInjectionStrategy('argument')
がある場合でも、メジャーバージョンの重大な変更としてより適切に伝達され、すべてのオンラインディスカッションとそのような変更にふさわしい騒動とともに提供される落とし穴になります。混乱をなくすのに役立ちます。
だから私はcukeの次のバージョンでそれをすることに投票し、そして...それがリリースされるまでmakedoingします。
カナリアバージョンまたは隠された旗は、私が使用する恐ろしいプロモーションであり、早い段階でフィードバックします
代替のFPインターフェイスを公開したいと思います。
次はどうですか? (async / awaitを使用して、それが約束に適していることを説明します)
import {initialize, Given, Before} from 'cucumber/fn'
// specify a function that returns the initial context:
initialize(async () => ({ a: 42 }))
Before({ timeout: 10 }, async (ctx) => {
await doStuff(ctx.a)
})
Given(/^a step passes with {number}$/, async (ctx, number) => {
const newA = await computeStuff(ctx.a, number)
// tell cucumber about the new context:
return Object.assign({}, ctx, { a: newA })
})
機能的なステップ定義を提供するcucumber-fpを一緒にハックしました。 私はすでにいくつかの改善のアイデアを持っています。 フィードバックを歓迎します!
この問題を閉じて、人々にその小さなライブラリを試してもらうことをお勧めします。 多分いつかそれをCucumber.jsに持ち込むことができます。
@jbprosテストで矢印関数を使用できるようにするために、さらに別の依存関係をインストールする必要はありません。
これは実際には、誰もが自分で実装するのは本当に簡単なはずです。 次のコードスニペットは、私にとっては(機能的に)非常にうまく機能しますが、使用できなくなるという大きな警告があります。
// Don't rely on `this` in step definitions. It's 2021 for crying out loud.
const definitionFunctionWrapper = (fn) =>
function(...args) {
return fn(...args.slice(0, -1), this);
}
すべてのステップ定義であるという警告は、追加のパラメーターのために次のエラーをログに記録するようになりました。
Error: function uses multiple asynchronous interfaces: callback and promise
to use the callback interface: do not return a promise
to use the promise interface: remove the last argument to the function
それを回避するためにパラメータを追加しようとすると、次のようになります。
function has 3 arguments, should have 1 (if synchronous or returning a promise) or 2 (if accepting a callback)
cucumber-jsに必要なのは、関数の引数のチェックを無効にするオプションだけであり、この問題は解消されます。 実際、cucumberは、ステップ定義関数の引数の数に基づいてコールバックが必要であると想定しているため、考え直してみると、テストもタイムアウトになるようです。 ただし、これは、返された約束を優先することで回避できます。
@andyearnshawご入力ございます。「stepdefsの矢印関数のみ」の依存関係について懸念をお聞きしました。 このライブラリは基本的に、ステートレスなステップ定義を取得するために私が個人的に使用するソリューションです。興味のある人のためにパッケージ化しただけです。
これは、コア内のこのような純粋なstepdef APIに関する進行中の議論に関する一時的な解決策と考えてください(信じられないかもしれませんが、4年以上続いています)。 私が言ったように、これはある時点でコアに着陸する可能性のある実験であり、実際にそれを使用している人々からのフィードバックを本当にいただければ幸いです。 それが十分な牽引力を得るならば、それはキュウリに統合するためのより良い議論になるでしょう。
また、他のいくつかの(小さな)便利な機能ツールを提供していることに注意してください: tap()
と強制された読み取り専用コンテキスト。
stepdef関数のアリティチェックは間違いなく私がこのlibで回避しなければならなかった問題です(かなり醜い方法で)。 CLIとプログラムの両方でオフにするオプションは、これ(および場合によっては他のユースケース)に非常に役立ちます。 やりたいのですが、今のところ時間は限られています。
その間、cucumber-fpからインスピレーションを得て、アリティチェックを修正してください。
@jbprosは素晴らしいです、そして私はあなたがそこに
@andyearnshawは正解です、説明してくれてありがとう。 私たちはこの考えを捨てるべきではないことに同意します、そしてこの問題を開いたままにしておくことはおそらく物事を透明に保つための良い方法です。
最も参考になるコメント
わかった。 しかし、誰もが関数型プログラマーであるわけではなく、重大な変更の導入を防ぐために、世界を最初のパラメーターとして渡すオプションを作成することについてどう思いますか?