#はじめに
この記事は、使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」を参考(引用)してます。
#describe / it / expect の役割
RSpecでよく使うdescribe, it, expectについて。
##describe テストのグループ化
・ describeはテストのグループ化を宣言する。
・ describeの中では複数のexample(it end ... do
)を記述できる。
##it テストをexampleにまとめる
・ itはテストをexampleという単位にまとめる役割をする。
・ it do ... end
の中のエクスペクテーション(期待値と実際の値の比較)が全てパスすればそのexampleはパスしたことになる。
・ it do ... end
のなかには一つのエクスペクテーション(expect
)を描く。(原則なので破っても良い)
##expect エクスペクテーション
itの部分で述べたエクスペクテーションがexpectにあたり、
expect(X).to eq Y
のような形で描く。
#使えると便利なcontext / before
###context
contextは条件でグループ化するときに使う。
RSpec.describe User do
describe '#greet' do
context '12歳以下の場合' do
it 'ひらがなで答えること' do
user = User.new(name: 'たろう', age: 12)
expect(user.greet).to eq 'ぼくはたろうだよ。'
end
end
context '13歳以上の場合' do
it '漢字で答えること' do
user = User.new(name: 'たろう', age: 13)
expect(user.greet).to eq '僕はたろうです。'
end
end
end
end
###before
・ beforeは共通の前準備をする時に使える。
・ before do ... end
に記述したものは、
example(it do ... end
)の実行前に呼ばれる。
・ beforeはテスト実行前の共通処理やデータのセットアップ等を行うことが多い。
RSpec.describe User do
describe '#greet' do
before do
@params = { name: 'たろう' }
end
context '12歳以下の場合' do
it 'ひらがなで答えること' do
user = User.new(@params.merge(age: 12))
expect(user.greet).to eq 'ぼくはたろうだよ。'
end
end
context '13歳以上の場合' do
it '漢字で答えること' do
user = User.new(@params.merge(age: 13))
expect(user.greet).to eq '僕はたろうです。'
end
end
end
end
beforeではインスタンス変数を使用している。
これはbefore
とit
では変数のスコープが異なるため。
####ネストしたdescribe, contextの中でのbefore
beforeはdescribe
やcontext
ごとに用意できる。
呼ばれる順番は、親、子の順。
RSpec.describe User do
describe '#greet' do
before do
@params = { name: 'たろう' }
end
context '12歳以下の場合' do
before do
@params.merge!(age: 12)
end
it 'ひらがなで答えること' do
user = User.new(@params)
expect(user.greet).to eq 'ぼくはたろうだよ。'
end
end
context '13歳以上の場合' do
before do
@params.merge!(age: 13)
end
it '漢字で答えること' do
user = User.new(@params)
expect(user.greet).to eq '僕はたろうです。'
end
end
end
end
#letの役割
###インスタンス変数の代わり
これまではbefore
ブロックに@params
という形でインスタンス変数を利用してきました。これをlet
を使って次のように書き換えられる。
#これを
before do
@params = { name: "たろう" }
end
#このように
let(:params) { { name: "たろう" } }
###ローカル変数の代わりにも
インスタンス変数だけでなく、ローカル変数もlet
でかける。
#これを
user = User.new(params)
#このように
let(:user) = User.new(params)
###letのメリット
before
がexpample(it do ... end
)の前に呼ばれるのに対し、let
は遅延評価される。let
は必要になるまで呼ばれない。
RSpec.describe User do
describe '#greet' do
let(:user) { User.new(params) }
let(:params) { { name: 'たろう', age: age } }
context '12歳以下の場合' do
let(:age) { 12 }
it 'ひらがなで答えること' do
expect(user.greet).to eq 'ぼくはたろうだよ。'
end
end
context '13歳以上の場合' do
let(:age) { 13 }
it '漢字で答えること' do
expect(user.greet).to eq '僕はたろうです。'
end
end
end
end
ここでは、
-
expect(user.greet).to eq
の部分でuser
をさがす。 -
let(:user) { User.new(params) }
が呼ばれ、
ここでparams
をさがす。 -
let(:params) { { name: 'たろう', age: age } }が
呼ばれ、次はage
をさがす。 -
let(:age) { 12 }
がよばれる。
のようにしてlet
が呼び出される。
###事前に実行されるlet!
let!
を使うとexample*の実行前にlet!
で定義した値が作られる。
RSpec.describe Blog do
#事前にblogが作られるためexpectでエラーにならない。
let!(:blog) { Blog.create(title: 'RSpec必勝法', content: 'あとで書く') }
it 'ブログの取得ができること' do
expect(Blog.first).to eq blog
end
end
#subjectの使い方
subject
を使えば同じエクスペクテーションを一つにまとめることができる。
#12歳以下のとき
expect(user.greet).to eq 'ぼくはたろうだよ。'
#13歳以上のとき
expect(user.greet).to eq '僕はたろうです。'
#上の二つを次のようにまとめる
subject { user.greet } #subjectで宣言し、
is_expected.to eq 'ぼくはたろうだよ。' #is_expected.toでテスト
さらに、it
を省略して、次のようにかける。
RSpec.describe User do
describe '#greet' do
let(:user) { User.new(name: 'たろう', age: age) }
subject { user.greet }
context '12歳以下の場合' do
let(:age) { 12 }
it { is_expected.to eq 'ぼくはたろうだよ。' }
end
context '13歳以上の場合' do
let(:age) { 13 }
it { is_expected.to eq '僕はたろうです。' }
end
end
end
#itのエイリアス specify, example
it
の代わりにspecify
とexample
が使える。
# それ(it)はユーザー名を返す
it 'returns user name' do
# ...
end
# 会社は社員を持つことを仕様として明記(specify)する
specify 'Company has employees' do
# ...
end
# fizz_buzzメソッドの実行例(example)
example '#fizz_buzz' do
# ...
end
#RSpecの高度な機能
shared_example と it_behaves_like
shared_example
と it_behaves_like
を使えば、example
の再利用ができる。
RSpec.describe User do
describe '#greet' do
#省略
shared_examples '大人のあいさつ' do
it { is_expected.to eq '僕はたろうです。' }
end
context '13歳の場合' do
let(:age) { 13 }
it_behaves_like '大人のあいさつ'
end
context '100歳の場合' do
let(:age) { 100 }
it_behaves_like '大人のあいさつ'
end
end
end
shared_example "foo" do ... end
で再利用したいexample
を定義し、it_behaves_like "foo"
で呼び出す。
###shared_context と include_context
shared_example
とit_behaves_like
のように、
context
の再利用には、shared_context
とinclude_context
が使える。
RSpec.describe User do
let(:user) { User.new(name: 'たろう', age: age) }
#これらの記述でlet(:age)の繰り返しをなくす
shared_context '12歳の場合' do
let(:age) { 12 }
end
shared_context '13歳の場合' do
let(:age) { 13 }
end
describe '#greet' do
subject { user.greet }
context '12歳以下の場合' do
include_context '12歳の場合'
it { is_expected.to eq 'ぼくはたろうだよ。' }
end
context '13歳以上の場合' do
include_context '13歳の場合'
it { is_expected.to eq '僕はたろうです。' }
end
end
describe '#child?' do
subject { user.child? }
context '12歳以下の場合' do
include_context '12歳の場合'
it { is_expected.to eq true }
end
context '13歳以上の場合' do
include_context '13歳の場合'
it { is_expected.to eq false }
end
end
end
shared_context "foo" do ... end
で再利用したいコンテキスト宣言し、contextの中でinclude_context "foo"
を使って呼び出す。
#スキップさせたいとき
RSpecではテストを一旦置いときたいときにスキップできる。
xを使う
it
やdescribe
の前にx
と書きxit
, xdescribe
とすることで、そのテストをスキップできる。(pending扱いになる。)
RSpec.describe '何らかの理由で実行したくないクラス' do
#xexample, xspecify も使える。
xit '実行したくないテスト' do
expect(foo).to eq bar
end
end
#describeをスキップ
xdescribe '四則演算' do
end
# contextをスキップ
xcontext '管理者の場合' do
end
テストは後で書く、中身のない it
it "foo" do ... end
のdo ... end
を省略することでpendingにすることができる。
RSpec.describe User do
describe '#good_bye' do
context '12歳以下の場合' do
#do ... end を省略
it 'ひらがなでさよならすること'
end
end