RSpecメモ
shared_examples
shared_examples
、shared_examples_for
、shared_context
shared_examples
、shared_examples_for
、shared_context
はすべて同じ動きをする。
let
やit
など共有したい塊がある場合に使う。
include_examples
、include_context
、it_behaves_like
、it_should_behave_like
include_examples
とinclude_context
は動きをみる感じではほとんど同じに見えるが、実装は異なるので何かしらの違いがあるかもしれない。
it_behaves_like
とit_should_behave_like
はrspec -f doc
を実行した時に出力される内容が異なるだけで動きとしては同じ動きをする。
以下のようなspecがあるとする。
shared_examples 'share' do
it { expect(1).to eq 1 }
end
include_examples 'share'
it_behaves_like 'share'
これは以下のように書いたのと同じ結果になる。
# include_examples 'share'
it { expect(1).to eq 1 }
# it_behaves_like 'share'
context 'behaves like share' do
it { expect(1).to eq 1 }
end
つまり、include_examples
はそのまま呼ばれた場所にshared_examples
の内容を埋め込むが、it_behaves_like
はcontext
(ないしdescribe
)でくくった状態で埋め込まれる。
この違いが影響するのは、以下のようなshared_examples
を定義した場合である。
shared_examples 'share' do |param|
let(:value) { param }
it { expect(value).to eq param }
end
このshared_examples
をinclude_examples
で二回呼び出してしまうと以下のような状態になる。
# 以下のように二回`include_examples`を呼んだ場合
# include_examples 'share', 'val1'
# include_examples 'share', 'val2'
# 以下のように定義したことと同じになる
# 同じ名前のletが複数ある場合、後発の定義が有効になるので、こっちのletは無効
let(:value) { 'val1' }
it { expect(value).to eq 'val1' } # このvalueは'val2'
let(:value) { 'val2' }
it { expect(value).to eq 'val2' }
フック
before
、after
、around
のフックがある。また引数として:each
(:example
でも同じ)、:all
(:context
でも同じ)を受け取る。デフォルトの指定は:example
でit
ごとにフックが実行される。:all
(:context
)を指定するとフックの記述されているdescribe
かcontext
ごとに一回だけフックが実行される。
around
は:each
(:example
)しか受け取れないので引数を指定する必要はないはず。
around { |e| puts 'around default'; e.run }
around(:each) { |e| puts 'around each'; e.run }
around(:example) { |e| puts 'around example'; e.run }
before { puts 'before default' }
before(:each) { puts 'before each' }
before(:example) { puts 'before example' }
before(:context) { puts 'before context' }
before(:all) { puts 'before all' }
after { puts 'after default' }
after(:each) { puts 'after each' }
after(:example) { puts 'after example' }
after(:context) { puts 'after context' }
after(:all) { puts 'after all' }
describe do
it { expect(1).to eq 1 }
it { expect(1).to eq 1 }
end
describe do
it { expect(1).to eq 1 }
end
を実行すると以下の通りになる。
before context
before all
around default
around each
around example
before default
before each
before example
after example
after each
after default
around default
around each
around example
before default
before each
before example
after example
after each
after default
around default
around each
around example
before default
before each
before example
after example
after each
after default
after all
after context
また、around
でインスタンス変数を定義してもspec内から参照できない。そもそも、 インスタンス変数を定義せずにlet
を使うべきである。
let
メモ化したメソッドの定義をする。let(:user) { create(:user) }
とした場合、user
が参照された時に1度だけcreate(:user)
が実行される。let!
を使うとbefore
フックと同タイミング(おそらくbefore
より前)に1度だけ実行される。
subject
デフォルトではdescribed_class
と一致する。let
と同じような形で定義でき、it
内ではsubject
で参照できる。
RSpec.describe User, type: :model do
describe '#name' do
# 以下3つのitは等価
it { expect(User.name).to eq 'name' }
it { expect(described_class.name).to eq 'name' }
it { expect(subject.name).to eq 'name' }
end
end
expect(subject)
の代わりにis_expected
とすることができる。また引数にシンボルを渡すとlet
で定義したのと同じようにその名前で参照できる。
RSpec.describe User, type: :model do
describe '#name' do
# 引数は省略できる
subject(:name) { User.name }
# 以下5つのitは等価
it { expect(User.name).to eq 'name' }
it { expect(described_class.name).to eq 'name' }
it { expect(subject).to eq 'name' }
it { expect(name).to eq 'name' }
it { is_expected.to eq 'name' }
end
end
let!
と同じようにsubject!
もある。
ヘルパーメソッド
Rubyのメソッド定義を使ってヘルパーメソッドを定義することができる。ヘルパーメソッドは定義したdescribe
やcontext
の中でのみ有効になる。
RSpec.describe User, type: :model do
def helper(arg)
expect(arg).to eq 1
end
it { helper(1) }
end