共有例の使用方法
共有例を使用すると、クラスやモジュールの振る舞いを記述することができます。共有例が宣言されると、共有グループの内容が保存されます。共有グループが実行される際には、別の例のグループのコンテキストが提供されます。
共有グループは、次のいずれかの方法で別のグループに含めることができます。
(include_examples "name" # include the examples in the current context
it_behaves_like "name" # include the examples in a nested context
it_should_behave_like "name" # include the examples in a nested context
matching metadata # include the examples in the current context
注意: 共有グループを含むファイルは、それを使用するファイルよりも先に読み込まれる必要があります。これに対処するための規約はありますが、RSpecは特別な処理(自動読み込みなど)を行いません。それを行うには、既存のスイートを壊す可能性のあるファイルの厳格な命名規則が必要です。
注意: 現在のコンテキストでパラメータ化された例を複数回含める場合、前のメソッド定義が上書きされ、最後の宣言が有効になります。したがって、次のような共有例(または共有コンテキスト)がある場合、
(RSpec.shared_examples "some example" do |parameter|
\# Same behavior is triggered also with either `def something; 'some value'; end`
\# or `define_method(:something) { 'some value' }`
let(:something) { parameter }
it "uses the given parameter" do
expect(something).to eq(parameter)
end
end
RSpec.describe SomeClass do
include_examples "some example", "parameter1"
include_examples "some example", "parameter2"
end
実際には次のようになります(最初の例が失敗することに注意してください):
(RSpec.describe SomeClass do
\# Reordered code for better understanding of what is happening
let(:something) { "parameter1" }
let(:something) { "parameter2" }
it "uses the given parameter" do
\# This example will fail because last let "wins"
expect(something).to eq("parameter1")
end
it "uses the given parameter" do
expect(something).to eq("parameter2")
end
end
このような微妙なエラーを防ぐために、同じコンテキスト内で同じ名前のメソッドを複数宣言すると警告が表示されます。この警告が表示された場合、最も簡単な解決策は、include_examples
をit_behaves_like
に置き換えることです。これにより、it_behaves_like
によって作成されるネストされたコンテキストによってメソッドの上書きが回避されます。
規約:
-
最も簡単なアプローチは、共有例を使用するファイルから明示的に共有例を含むファイルを要求することです。RSpecは
spec
ディレクトリをLOAD_PATH
に追加するため、require 'shared_examples_for_widgets'
と記述することで、#{PROJECT_ROOT}/spec/shared_examples_for_widgets.rb
にあるファイルを要求することができます。 -
一つの規約は、共有例を含むファイルを
spec/support/
に配置し、そのディレクトリ内のファイルをspec/spec_helper.rb
から要求することです。
Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
これは、rspec-rails
で生成されるspec/spec_helper.rb
ファイルに含まれていましたが、テストスイートの起動時間を短く保つために、このようなディレクトリ内のすべてのファイルを自動的に読み込まない方が良いアイデアです。1つのスペックファイルのみを実行する場合、不要な依存関係の読み込みや不要なセットアップは、最初の例が実行されるまでにかかる時間に対して、かなりの、目に見える効果をもたらすことがあります。
- 共有グループが含まれるすべてのグループが同じファイルに存在する場合は、そのファイル内で共有グループを宣言します。
同じファイル内の2つのグループに含まれる共有例グループ
Given ファイル名が "collection_spec.rb" のファイルがあるとき、以下の内容を含む:
require "set"
RSpec.shared_examples "a collection" do
let(:collection) { described_class.new([7, 2, 4]) }
context "initialized with 3 items" do
it "says it has three items" do
expect(collection.size).to eq(3)
end
end
describe "#include?" do
context "with an item that is in the collection" do
it "returns true" do
expect(collection.include?(7)).to be(true)
end
end
context "with an item that is not in the collection" do
it "returns false" do
expect(collection.include?(9)).to be(false)
end
end
end
end
RSpec.describe Array do
it_behaves_like "a collection"
end
RSpec.describe Set do
it_behaves_like "a collection"
end
When rspec collection_spec.rb --format documentation
を実行すると、
Then すべての例がパスするべきです
And 出力には以下が含まれるべきです:
Array
behaves like a collection
initialized with 3 items
says it has three items
#include?
with an item that is in the collection
returns true
with an item that is not in the collection
returns false
Set
behaves like a collection
initialized with 3 items
says it has three items
#include?
with an item that is in the collection
returns true
with an item that is not in the collection
returns false
ブロックを使用して共有グループにコンテキストを提供する
Given ファイル名が "shared_example_group_spec.rb" のファイルがあるとき、以下の内容を含む:
require "set"
RSpec.shared_examples "a collection object" do
describe "<<" do
it "adds objects to the end of the collection" do
collection << 1
collection << 2
expect(collection.to_a).to match_array([1, 2])
end
end
end
RSpec.describe Array do
it_behaves_like "a collection object" do
let(:collection) { Array.new }
end
end
RSpec.describe Set do
it_behaves_like "a collection object" do
let(:collection) { Set.new }
end
end
When rspec shared_example_group_spec.rb --format documentation
を実行すると、
Then すべての例がパスするべきです
And 出力には以下が含まれるべきです:
Array
behaves like a collection object
<<
adds objects to the end of the collection
Set
behaves like a collection object
<<
adds objects to the end of the collection