5

私は自分の仕様をクリーンで DRY に保とうとしていますが、API のどのバージョンがテストされているかを除いて同一の API のテストがいくつかあります。次のようなものを使用して、仕様を簡単に繰り返すことができます。

%w( v1 v2 ).each do |version|
  describe "Query #{version} API" do
    it "responds with JSON"
      # make the call using the version 
    end
  end
end

しかし、私はもう少しクリーンなものが欲しいので、このメソッドを書きました:

module RepetitivelyDescribe
  def repetitively_describe(*args, &example_group_block)
    options = args.extract_options!
    options.delete(:for).each do |item|
      item_args = args.collect(&:dup) + [options.dup]
      item_args[0] << " [#{item}]"

      describe(*item_args) do
        example_group_block.call item
      end
    end
  end
end

RSpec::Core::ExampleGroup.extend RepetitivelyDescribe

そして、私のテストは次のようになります。

repetitively_describe "Query API", :for => %( v1 v2 ) do |version|
  it "responds with JSON"
    # make the call using the version 
  end
end

これは少し衒学的なことだと思いますが、インデントのレベルが 1 つ少ないので、この呼び出しを頻繁に行う場合は、より簡潔にしたいと思います。

しかし、もちろん、それは私が望むようには機能していません。describemy 内の呼び出しrepetitively_describeは RSpec 出力に記録されません (ドキュメンテーション形式の出力を使用する場合)。基本的に、そのレベルのコンテキストは失われます (describeブロックの外側と内側のrepetitively_describeブロックは保持されます)。

必要に応じて、より詳細なサンプルコードが要点にあります。これがうまくいかない理由の手がかりはありますか?

4

1 に答える 1

6

したがって(すでに知っていることを繰り返している場合はお詫びします)、describe / contextを呼び出すたびに、rspecは現在のサンプルグループクラス(最終的にはのサブクラスRSpec::Core::ExampleGroup)のサブクラスである新しいクラスを作成しmodule_eval、そのクラスのコンテキストでブロックします。私が走ったら

describe "foo" do
  puts "#{self}; #{self.superclass}"
  describe "behaviour 1" do
    puts "#{self}; #{self.superclass}"
    context "with x" do
      puts "#{self}; #{self.superclass}"
    end
  end
end

次に、出力は

#<Class:0x007fb772bfbc70>; RSpec::Core::ExampleGroup
#<Class:0x007fb772bfb180>; #<Class:0x007fb772bfbc70>
#<Class:0x007fb772bfa5f0>; #<Class:0x007fb772bfb180>

itrspecを呼び出すと、Exampleオブジェクトを作成し、それを自分自身(現在のサンプルグループ)のクラスインスタンス変数に追加します。rspecはまた、現在のサンプルグループをサンプルのメタデータに固定します。このサンプルグループのツリーをたどると、サンプルの完全な説明が得られます。

メソッドrepetitively_describeがを呼び出すdescribeので、selfを呼び出す時点で、example_group_block.call item実際に新しく作成されたサンプルグループになります。procが評価されると、もちろん、self呼び出されたときの値が記憶されるため、it繰り返し_describeのときに現在のサンプルグループに対して呼び出しが行われます(コード全体でselfの値を確認するためにいくつかの呼び出しを振りかけることで簡単に検証できます) )。同様に、describeを呼び出すと、によって作成されたものではなく、外側のサンプルグループの子としてサンプルグループが追加されrepetitively_describeます。

もちろんあなたがする必要があるのはexample_group_block、自己の正しい価値を維持することです。

module RepetitivelyDescribe
  def repetitively_describe(*args, &example_group_block)
    options = args.extract_options!
    options.delete(:for).each do |item|
      item_args = args.collect(&:dup) + [options.dup]
      item_args[0] << " [#{item}]"

      describe(*item_args) do
        class_exec(item, &example_group_block)
      end
    end
  end
end

この変更で

describe('foo') do
  repetitively_describe "Query API", :for => %w( v1 v2 ) do |version|
    it "responds with JSON"
  end
end.descendant_filtered_examples.collect(&:full_description)

変更前では["foo Query API [v1] responds with JSON", "foo Query API [v2] responds with JSON"]なく出力。["foo responds with JSON", "foo responds with JSON"]

于 2012-06-02T10:35:22.067 に答える