Cucumber-js: ステップ定義の引数チェックを緩和するオプション

作成日 2016年02月12日  ·  14コメント  ·  ソース: cucumber/cucumber-js

こんにちは、私はステップ引数に対して行われている厳密なチェックの価値を理解していますが、この制御をユーザーに戻す必要もあると感じています。

たとえば、私の場合、ステップの呼び出しの前後にいくつかのジェネリック処理ロジックを追加したいステップ実装関数の周りにジェネリックラッパー関数を実装したいと思います。 このジェネリックラッパーの場合、渡された引数にアクセスする必要があるため、明示的なパラメーターを宣言するのではなく、arguments配列にアクセスする必要があります。 現在、キュウリはパラメータの厳密なチェックを行うため、これを行うのを妨げるだけです。

設定されていない場合はfalseと見なされるskipStrictParameterCheckのようなものと呼ばれる別の構成パラメーターをオプションオブジェクトに追加することを提案したいと思います。 そうすれば、最も一般的な使用法では、デフォルトの動作は厳密なチェックになりますが、フレームワークを使用してフレームワークをさらに構築したい他の人にとっては、JavaScriptの動的機能の一部を活用する柔軟性が得られます。

全てのコメント14件

私は同じリクエストをしました。#445を参照してください:)

@ riaan53 、ええ、私はそれを見ましたが、残念ながら、おそらくいくつかの正しい理由のためにリクエストが拒否されました。 私はその理由に同意しませんが、より大きなフレームワークを構築する一環として、ユーザーがそれを悪用しない限り、この機能は重要であると思います。したがって、これを厳密にオプションの構成にすることをお勧めします。

あなたが話しているこの一般的な処理ロジックのいくつかは何ですか?

もちろん。 あるユースケースでは、すべての開発者がステップ定義で式参照を使用できるようにしたいと思います。 例:「ユーザー$ {admin}がシステムにログインしたとき」。

この場合、$ {admin}はおそらくJSONファイルから解決され、解決の責任はステップ定義を実装する際の開発者にあります。 しかし、実際にそのような種類のプロパティ解決を見る場合、これは、開発者がまったく気付かない一般的なコードによって実行できます。

これを可能にするために、Cucumberによって挿入された生の引数を受け入れ、それらを解決してから、解決された値を実際のステップ実装に挿入する、開発者ステップ実装のジェネリック関数ラッパーを簡単に作成できます。

現在、Cucumberの検証により、関数に適切な数のパラメーターがあることが確認されるため、このようなジェネリック関数を作成することはできません。私の場合、ジェネリックラッパー関数は名前付き引数を受け入れず、 'arguments'オブジェクト。

私が理にかなっていることを願っています。 現在のプロジェクトには、ステップの実装に関する一般的なラッパー関数が必要になる他のユースケースもあります。

ステップ引数変換を実装した場合、問題は解決します: https

ああ、そうです、照応解析の問題に対処できる可能性があります。 ただし、他の理由で、ステップの実装の周りに汎用ラッパー関数を配置する必要があります。

たとえば、1つの理由は、フレームワークとしてCucumberを使用してProtractorを使用しており、Protractorでは、WebDriver制御フローにカスタムPromiseを登録して、これらのカスタムPromiseが解決されるのを熱心に待ってから次のステップに進む必要があることです。

Cucumberはpromiseが解決されるのを待ちますが(ステップ実装から返された場合)、明らかにWebDriverに対応しておらず、ほとんどの場合、返されたpromiseをWebDriverに登録するために各ステップ実装にコードを追加する必要があります。

これもまた、Cucumberによってパラメーターチェックを緩和する必要があるジェネリック関数ラッパーを介して対処するのは非常に簡単です。

私は今、この問題に数回遭遇しました。

最も顕著なものは、すべてのThenステップにretryヘルパーを実装することです。

cucumber.Then = function(match, callback) {
  const retryingCallback = (...args) => retry(async () => await callback(...args));
  cucumber.Then(match, retryingCallback);
};

このヘルパーを使用して、結果整合性のあるバックエンドのタイミングの問題に対処します。 基本的に、y秒が経過するか、コールバックが通過するまで、x秒ごとにコールバックを実行します。

悲しいことに、これは原因になります

function has 0 arguments, should have 1 (if synchronous or returning a promise) or 2 (if accepting a callback)

私が現在使用している代替手段は、 Thenステップごとにヘルパーを呼び出すことです。これにより、大量のコードが重複します。

this.Then(/^I see that "([^"]*)" does not have a destination$/, async clientName => {
  return retry(async () => {
    const client = await homeView.clientByName(clientName);
    expect(client.destinationName).to.not.exist;
  });
});

もう1つのケースは、ログイン用のヘルパー関数が必要な、より単純なケースです。

function loggedIn(username, func) {
  return (...args) => {
    await accounts.login(username);
    return func(...args)
  };
}

this.Then(/^someone logged in as "([^"]*)" sees a destination named "([^"]*)"$/, loggedIn(assert.destinationExists));

また、これは私に多くのコード重複を節約するでしょう。

最後に、ある時点で、すべての受け入れテストを実行するスイートを追加したいと思っていますが、 Thenコールバックの前にサーバーを再起動します(サーバーの再起動によって問題が発生しないようにするため)。 繰り返しますが、多くの重複があります。

PS再試行ヘルパー:

const patience = 250;
const interval = 5;

function delay(time) {
  return new Promise(function (fulfill) {
    setTimeout(fulfill, time);
  });
}

async function attempt(start, func) {
  const attemptDate = new Date();
  try {
    return await func();
  } catch (errr) {
    const timeElapsed = attemptDate.getTime() - start.getTime();
    if (timeElapsed < patience) {
      await delay(interval);
      return await attempt(start, func);
    } else {
      throw errr;
    }
  }
}

export async function retry(func) {
  const start = new Date();
  return await attempt(start, func);
}

_編集_

それを回避するために私の方法をハックしようとしました:

function splat(func) {
  return (one, two, three, four, five, six, seven, eight, nine, ten) => {
    if (typeof ten !== 'undefined') {
      return func(one, two, three, four, five, six, seven, eight, nine, ten);
    } else if (typeof nine !== 'undefined') {
      return func(one, two, three, four, five, six, seven, eight, nine);
    } else if (typeof eight !== 'undefined') {
      return func(one, two, three, four, five, six, seven, eight);
    } else if (typeof seven !== 'undefined') {
      return func(one, two, three, four, five, six, seven);
    } else if (typeof six !== 'undefined') {
      return func(one, two, three, four, five, six);
    } else if (typeof five !== 'undefined') {
      return func(one, two, three, four, five);
    } else if (typeof four !== 'undefined') {
      return func(one, two, three, four);
    } else if (typeof three !== 'undefined') {
      return func(one, two, three);
    } else if (typeof two !== 'undefined') {
      return func(one, two);
    } else if (typeof one !== 'undefined') {
      return func(one);
    } else {
      return func();
    }
  };
}

cucumber.Then = function(match, callback) {
  const retryingCallback = splat((...args) => retry(async () => await callback(...args)));
  cucumber.Then(match, retryingCallback);
};

しかし

function has 10 arguments, should have 1 (if synchronous or returning a promise) or 2 (if accepting a callback)

私を悲しいパンダにします

これは、長さを保持するために関数をラップする例です。 これを実行する小さなノードモジュールがあればいいのにと思います。

提案をありがとう! ステップ定義ごとに引数の数を指定すると、それは機能しますよね?

例えば。

this.Then(/^someone logged in as "([^"]*)" sees a destination named "([^"]*)"$/, createProxy(loggedIn(assert.destinationExists), 2));

私にとって最も差し迫った問題は、複数のステップ定義に汎用ミドルウェアを追加できないことです。 createProxyようなものは、ミドルウェアの登録を許可するオブジェクトにして、すべてのステップ定義で引数の数を指定すれば、おそらく機能する可能性があります。 (私の最初の例をよく見ると、 retry関数がそれをラップするため、 createProxy直接使用できないことがわかります。逆のはずですが、 createProxyは、各コールバックの引数の数を知りません)

それでも、エラーをオフに切り替えることができるのに比べて、本当に厄介な感じがします。 :無垢:

proxyLengthを渡す代わりに、ラップしている関数を渡してfunction.lengthを使用する、その関数のバリアントを使用できると思います。

素晴らしい提案、ありがとう!

私は次のようなものを動作させました:

cucumber.Then = function(match, callback) {
  cucumber.Then(match, retryProxy(callback));
};

function retryProxy(func) {
  const numberOfArgs = func.length;
  switch (numberOfArgs) {
    case 0: return () => retry(func);
    case 1: return (a) => retry(func, a);
    case 2: return (a, b) => retry(func, a, b);
    case 3: return (a, b, c) => retry(func, a, b, c);
    case 4: return (a, b, c, d) => retry(func, a, b, c, d);
    case 5: return (a, b, c, d, e) => retry(func, a, b, c, d, e);
  }
}

それが解決しない2つのことは、ログインヘルパーの場合とデフォルトのパラメーターを許可することですが、私はこれら2つを回避することができます。

ステップ定義を調整せずにミドルウェアを追加できるようになりました。

@thomasvanlankveldご存知のとおり、特定の関数の長さを与えるために関数をラップするこのライブラリを見つけました: https

@ sushil-rxrジェネリック関数ラッパーのように、元の関数の長さを保持できますか?

2.0.0-rc.1 、ジェネリック関数ラッパーを追加できるようになりました。 また、元の関数の長さを保持する機能が組み込まれています。

このスレッドは、閉じられた後に最近のアクティビティがないため、自動的にロックされています。 関連するバグについては、新しい問題を開いてください。

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