2

以下の複雑な期待を共有する RSpec の例がいくつかあります。これらの例では、配列recordsと浮動小数点数min_long, max_long,min_latmax_lat異なります。

  expect(records).to all have_attributes(
    shape: have_attributes(
      exterior_ring: have_attributes(
        points: all(
          have_attributes(
            longitude: be_between(min_long, max_long),
            latitude: be_between(min_lat, max_lat)
          )
        )
      )
    )
  )

(期待値は、それぞれのテストが生成したすべてのレコードが、テスト固有の境界ボックスに完全に含まれる形状 (私の場合はRGeo Polygon ) を持っているかどうかをチェックします。)

繰り返しを減らし、名前をつけて複雑な期待の意図をより明確にするために、それをメソッドに抽出しました。

def expect_in_bbox(records, min_long, max_long, min_lat, max_lat)
  expect(records).to all have_attributes(
    shape: have_attributes(
      exterior_ring: have_attributes(
        points: all(
          have_attributes(
            longitude: be_between(min_long, max_long),
            latitude: be_between(min_lat, max_lat)
          )
        )
      )
    )
  )
end

これは正常に動作しますが、今ではそのメソッドを呼び出す必要があります。

expect_in_bbox(valid_records, 12.55744, 12.80270, 51.36250, 51.63187)

私の例では。

これは、RSpec の仕様 DSL では異質に見えます。書ける方がいいです

expect(valid_records).to be_in_bbox(12.55744, 12.80270, 51.36250, 51.63187)

また

expect(valid_records).to all be_in_bbox(12.55744, 12.80270, 51.36250, 51.63187)

代わりは。

これを達成するための推奨される方法はありますか?

RSpec のマッチャー エイリアシング機能は、マッチャー名を他のマッチャー にマップするだけで、引数を使用した完全なマッチャー呼び出しではないように見えるため、これには使用できないと思い ます。とはいえ、おそらく のoptions引数はalias_matcherそのためのものでしょうか?

もちろん、カスタム マッチャーを実装することもできますが、その場合、既存のマッチャーで構成されていることと矛盾するブール値を返す実装を提供する必要があるでしょう。all(難しいというわけではありませんが、や などを使った実装が好きbe_betweenです。)

最後に、要素のクラスにモンキー パッチを適用して属性valid_recordsを持たせin_bbox?(min_long, max_long, min_lat, max_lat)、RSpec が対応するbe_in_bbox(min_long, max_long, min_lat, max_lat)マッチャーを自動的に提供するようにすることもできます。

4

2 に答える 2

3

確かにあなたはそれを行うことができます。ヘルパーメソッドにします。

ヘルパー メソッド

これらは通常の Ruby メソッドです。それらは、任意のサンプル グループで定義できます。これらのヘルパー メソッドは、それらが定義されているグループおよびそのグループ内にネストされたグループの例に公開されますが、親または兄弟グループには公開されません。

def be_in_bbox(min_long, max_long, min_lat, max_lat)
  all(
    have_attributes(
      shape: have_attributes(
        exterior_ring: have_attributes(
          points: all(
            have_attributes(
              longitude: be_between(min_long, max_long),
              latitude: be_between(min_lat, max_lat)
            )
          )
        )
      )
    )
  )
end

そのメソッドを に保存されたわかりやすい名前のファイルに入れることをお勧めしますspec/support。などの可能性がありspec/support/rgeo_matchers.rbます。書かれているように、これはヘルパー on を定義しmain、それを に混ぜ合わせてKernel、Ruby のすべてのオブジェクトで利用できるようにします。このヘルパー ファイルが必要なすべてのスペック ファイルで必要であることを確認する必要がありますrequire 'support/rgeo_matchers'

でヘルパーを定義する代わりに、グローバルなリークを防ぐためにモジュールに配置することmainをお勧めします。

module MyProject
  module RGeo
    module Matchers
      def be_in_bbox(...)
        # ...
      end
    end
  end
end

マッチャーはモジュール内にあるため、ブロックinclude MyProject::RGeo::Matchers内に追加する必要があります。RSpec.describe

別の方法は、それを共有コンテキストにすることです:

RSpec.shared_context "RGeo matchers" do
  def be_in_bbox(...)
    # ...
  end
end

共有コンテキストでは、 :include_contextの代わりに使用する必要があります。includeinclude_context "RGeo matchers"

複雑なマッチャー

あなたが説明するマッチャーはかなりネストされていますが、それがドメインモデルに適合し、一貫した「ユニット」を説明している場合、それは私の本では受け入れられます。「1 つのことをテストする」ということは、必ずしも 1 つの属性のみをテストすることを意味するわけではありません。「一貫したコンセプト」または「ユニット」をテストすることを意味します。それが何を意味するかは、ドメイン モデルによって異なります。

あなたが示したように、合成可能なマッチャー複合的な期待と組み合わせることで、カスタムマッチャーを書くためのシンプルで有効な代替手段が提供されます。

代替案

あなたの提案allに従って、マッチャーが「境界ボックス内」にあることのみを説明するように、おそらくヘルパーから を削除します。

def be_in_bbox(min_long, max_long, min_lat, max_lat)
  have_attributes(
    # ...
  )
end

これにより、マッチャーがより再利用しやすくなります。それは本当に「1つのこと」を説明しているためです(たとえば、「バウンディングボックスの内側」)。これにより、スタンドアロンのマッチャーとして使用したり、他のマッチャーと組み合わせたりすることができます。

it "returns matching bounding boxes" do
  expect(valid_records).to all be_in_bbox(12.55744, 12.80270, 51.36250, 51.63187)
end

it "is in bounding box defined by [(12.55744..12.80270), (51.36250..51.63187)]" do
  expect(generated_box).to be_in_bbox(12.55744, 12.80270, 51.36250, 51.63187)
end
于 2015-05-29T22:39:13.753 に答える