rspecの挙動が分かりづらかったので簡単にまとめてみました。
Gemfile.lock
rspec (3.6.0)
rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-core (3.6.0)
rspec-support (~> 3.6.0)
rspec-expectations (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0)
rspec-mocks (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0)
rspec-rails (3.6.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-support (~> 3.6.0)
rspec-support (3.6.0)
検証コード
create(:model)
はfactorygirl
で何かしらのApplicationRecord
のオブジェクトを作る想定
require 'rails_helper'
RSpec.describe do
let(:model) do
m = create(:model)
p 'outer let:' + m.id.to_s
m
end
before do
m = create(:model)
p 'outer before:' + m.id.to_s
end
before(:each) do
m = create(:model)
p 'outer before(each):' + m.id.to_s
end
before(:all) do
m = create(:model)
p 'outer before(all):' + m.id.to_s
end
context do
let(:model) do
m = create(:model)
p 'inner let:' + m.id.to_s
m
end
before do
m = create(:model)
p 'inner before:' + m.id.to_s
end
before(:each) do
m = create(:model)
p 'inner before(each):' + m.id.to_s
end
before(:all) do
m = create(:model)
p 'inner before(all):' + m.id.to_s
end
subject do
m = model
p 'subject1:' + m.id.to_s
m
end
example { expect(subject.id).to eq(model.id) }
it do
m = model
p 'it:' + m.id.to_s
expect(subject.id).to eq(m.id)
end
end
context do
subject do
m = model
p 'subject2:' + m.id.to_s
m
end
example { expect(subject.id).to eq(model.id) }
end
end
とした場合、アウトプットは下記の通りになる
"outer before(all):1"
"inner before(all):2"
"outer before:3"
"outer before(each):4"
"inner before:5"
"inner before(each):6"
"inner let:7"
"subject1:7"
"outer before:8"
"outer before(each):9"
"inner before:10"
"inner before(each):11"
"inner let:12"
"it:12"
"subject1:12"
"outer before:13"
"outer before(each):14"
"outer let:15"
"subject2:15"
まとめ
- テストの実行単位は
it
orexample
であり、基本的に実行単位ごとに変数は初期化される(以下、このテスト実行単位をテストプロセスと表記します) -
before
メソッドはブロックのスコープ内で有効 -
before(:all)
はブロック内で一度実行される(ブロック内にテストプロセスが複数あっても実行は一度のみ) -
before
とbefore(:each)
は同じ。ブロック内のテストプロセスごとに実行される -
subject
はlet(:subject)
の略。テスト対象を表す変数。expect(subject).to
をis_expected.to
と略せる。 -
let
について - 遅延評価変数。ブロック内でコールされたタイミングでletブロック内の処理を行い結果をletの引き数の変数に格納
- 同じブロック内で複数回コールされる場合、初回にletブロック内の処理を行いその結果のオブジェクトを、2回目以降は初回の実行時に作成されたオブジェクトを返却する
- ブロックのスコープ内とスコープ外の2箇所で同じ名前のlet変数が定義されていた場合、ブロック内のletだけが評価される
おまけ(letとlet!)
require 'rails_helper'
RSpec.describe do
let(:model) do
m = create(:model)
p 'outer let:' + m.id.to_s
m
end
context do
let!(:dummy_model) do
m = create(:model)
p 'dummy let!:' + m.id.to_s
m
end
subject do
m = model
p 'subject:' + m.id.to_s
m
end
example { expect(subject.id).to eq(model.id) }
it do
m = model
p 'it:' + m.id.to_s
expect(subject.id).to eq(m.id)
end
end
end
"dummy let!:1"
"outer let:2"
"subject:2"
"dummy let!:3"
"outer let:4"
"it:4"
"subject:4"
-
let!
は変数がコールされなくてもテストプロセスのはじめに実行される(before
の前か後かはコードの記述順で決まる)