RSpecマッチャーメモ
?
メソッドのショートカット
User#owner?
やUser#has_books?(book)
のようなメソッドをテストする場合を考える。
そのまま書くと以下のようになる。
subject(:user) { User.new }
describe '#owner?' do
# ...
it 'オーナーになる' do
expect(user.owner?).to eq true
end
end
describe '#has_books?' do
# ...
it '本を持っている' do
expect(user.has_books?(Book.first)).to eq true
end
end
しかし、RSpecは?
メソッドに対してショートカットのマッチャーを用意しているので以下のように書ける。
subject(:user) { User.new }
describe '#owner?' do
# ...
it 'オーナーになる' do
is_expected.to be_owner
end
end
describe '#has_books?' do
# ...
it '本を持っている' do
is_expected.to have_book(Book.first)
end
end
has_book?
メソッドはbe_has_book
でも良いが英語として意味が通らないのでhave_book
マッチャーが用意される(be_has_book
が使えないわけではない)。
ただまあ、上記の例の場合だとsubject { target_user.owner? }
のようにしてcontext
でtarget_user
を差し替える方が見通しが良いと思う。
kind_of?
、instance_of?
be_kind_of
、be_instance_of
も使えるが、be_kind_of
にはbe_a_kind_of
、be_a
、be_an
のエイリアスが、be_instance_of
にはbe_an_instance_of
のエイリアスがある。
all
配列の全ての要素に対してマッチャーを適用する。
subject { [1, 3, 5] }
it '全て奇数' do
subject.each do |item|
expect(item).to be_odd
end
end
は、以下のように書ける。
subject { [1, 3, 5] }
it '全て奇数' do
is_expected.to all(be_odd)
end
change
メソッドの戻り値の変化を確認するマッチャー。
subject(:user) { create(:user, name: 'suzuki') }
it do
# fromからtoに変わったことを確認
expect { user.name = 'yamada' }.to change { user.name }.from('suzuki').to('yamada')
# 変わったことだけを確認
expect { user.name = 'sato' }.to change { user.name }
# 変化量を確認
expect { user.destroy }.to change { User.count }.by(-1)
end
contain_exactly
順序はともかく、指定した要素で構成された配列であることを確認する。
expect([:a, :b, :c]).to contain_exactly(:c, :a, :b) # OK
expect([:a, :b, :c]).to contain_exactly(:c, :b) # NG
expect([:a, :b, :c]).to contain_exactly(:c, :b, :a, :d) # NG
cover
指定した要素がRangeに含まれるか確認する。
expect(5..10).to cover(7, 8, 10)
start_with
指定した内容で始まるか確認する。
expect([2, 3, 4, 5, 10]).to start_with(2, 3, 4)
expect('abc def xyz').to start_with('abc def')
end_with
指定した内容で終わるか確認する。
expect([2, 3, 4, 5, 10]).to end_with(4, 5, 10)
expect('abc def xyz').to end_with('def xyz')
have_attributes
モデルのインスタンスを比較する際にまとめて確認する。
subject(:user) { build(:user) }
it do
# 下記の代わりに
# expect(user.name).to eq '...'
# expect(user.email).to eq '...'
# 以下のように書ける
expect(user).to have_attributes(name: '...', email: '...')
end
include
配列、文字列、ハッシュのいずれかに対して、指定した条件にマッチするものが含まれるか確認する。
RSpec.describe Array do
subject { [1, 3, 5] }
it do
is_expected.to include(1, 3)
is_expected.to_not include(1, 7) # 7は含まれないのでnot
is_expected.to include(be_odd)
is_expected.to include(be <= 5)
end
end
RSpec.describe String do
subject { 'abc def xyz' }
it do
is_expected.to include('xyz')
is_expected.to include('def', 'xy', 'a')
end
end
RSpec.describe Hash do
subject { { a: 1, b: 2 } }
it do
is_expected.to include(a: 1)
end
end
match
文字列または正規表現に対して、指定した条件でmatch?
メソッドがtrueを返すか確認する。
expect('abc').to match('b')
expect('abc').to match(/[a-z]+/)
expect(/b/).to match('abc')
expect(/[a-z]+/).to match('abc')
raise_error
例外が発生することを確認する。例外クラスかメッセージを指定することができる。
expect { 1 / 0 }.to raise_error
expect { 1 / 0 }.to raise_error(ZeroDivisionError)
expect { 1 / 0 }.to raise_error('divided by 0')
expect { 1 / 0 }.to raise_error(/0/)
expect { 1 / 0 }.to raise_error(ZeroDivisionError, 'divided by 0')
and
、or
マッチャーはand
やor
で繋ぐことができる。
expect(1).to be_odd.and be_integer
expect(2).to be_odd.or eq 2
expect([0,1,2,3,4,5]).to include(be_zero.or eq 10)