どの段階でテストコードが評価されているか注意する。
expect
と is_expected
何が違うの??な人のための記事です。
subject
を使うのはどういうとき?
定義元のコメントを見てみると・・
# Declares a `subject` for an example group which can then be wrapped
# with `expect` using `is_expected` to make it the target of an
# expectation in a concise, one-line example.
# ...
def subject(name=nil, &block)
if name
let(name, &block)
alias_method :subject, name
self::NamedSubjectPreventSuper.__send__(:define_method, name) do
raise NotImplementedError, "`super` in named subjects is not supported"
end
else
let(:subject, &block)
end
end
↑is_expected
を使って簡潔な一行 example を書くため、らしい。
なのでsubject と is_expected はペアで使う。
subject
の実態は要するに let
(※letがわからない場合はここが詳しい解説)
そして let!
ではないので、この時点ではまだ評価されてない。
is_expected
は何なのか?
# Wraps the `subject` in `expect` to make it the target of an expectation.
# ...
# Designed to read nicely for one-liners.
def is_expected
expect(subject)
end
インスタンス変数の subject
を呼び出してる。
なのでこいつをペアで呼んであげると、subjectに渡したブロックが評価される。
特にハマリポイントはない王道パターンの使い方
RSpec.describe BooksController do
describe "GET 'index'" do
subject(:action) { get 'index' }
it { is_expected.to be_success } # OK
end
end
expect
を使いたい時は?
is_expected じゃなくて expect だと何が違う?
# さっきのテストコードの続き
let(:param) { {title: 'My Book'} } # 適当にcreateが成立するパラメータが定義されてるとする
# ステータスコードチェックしてみる
describe "POST 'create'" do
# json のリクエストみたいに成功したら 201 (:craeted)が戻るコントローラーを例に
subject(:action) { post 'create', params }
it { expect(subject.status).to eq 201 } # 1.OK
it { expect(action.status).to eq 201 } # 2.OK
it { expect(response.status).to eq 201 } # 3.NG
end
それぞれ何をやってるか
1. expect(subject.status).to eq 201
subject -> let(:subject)で定義されたブロック(上の例で { post 'create' }
のとこ)を評価して、 subject.status
をチェック
2. expect(action.status).to eq 201
subject の name として action
が渡ってるので、 ここでは subject == action
1と同じ。
3. expect(response.status).to eq 201
subject を評価していないので、 インスタンス変数 response の値は初期値のまま変わってない。
のでエラー
(status==200が初期値なので、GET#index の所で 200 チェックしてると、この現象に気づきづらい)
通したい場合
expect(subject)
で評価
describe "POST 'create'" do
subject(:action) { post 'create', params }
it do
expect(subject) # ここで評価
expect(response.status).to eq 201
end
end
subject
だけでも良い
describe "POST 'create'" do
subject(:action) { post 'create', params }
it do
subject # ここで評価
expect(response.status).to eq 201
end
end
subject!
let, let!の関係から推測・・
describe "POST 'create'" do
subject!(:action) { post 'create', params }
it do
expect(response.status).to eq 201
end
end
でもOK :)
statusチェックしたいだけなら is_expected
でもできる
it { is_expected.to have_http_status(201) } # OK
it { is_expected.to have_attributes(status: 201) } # OK
・・のでこんなとこでハマる人居ないはず;;
assigns チェックの時はこんな感じ
let!(:books) { FactoryGirl.create_list(:book, 10) }
subject(:action) { get 'index' }
it do
expect(subject)
expect(assigns(:books)).to eq(books)
end
let!(:books) { FactoryGirl.create_list(:book, 10) }
subject(:action) { get 'index' }
it do
subject
expect(assigns(:books)).to eq(books)
end
let!(:books) { FactoryGirl.create_list(:book, 10) }
subject!(:action) { get 'index' }
it do
expect(assigns(:books)).to eq(books)
end
上の「通したい場合」のどのパターンでも良い。