はじめに
自分はRSpecについての理解が全然ない状態で勉強してある程度は使えるようになりました!
初心者なりに実践的な使い方やコツをまとめてみたのでよかったらご覧ください。
テクニック・知識集
####TDDとは
テスト駆動開発(Test-Driven Development)の略で、テストファーストなプログラムの開発手法のこと。
少しわかりやすく言うと、プログラムの実装前にテストコードを書き、そのテストコードに適合するように実装とリファクタリングを進めていく方法です。
####is_expected
exampleを一行で書くために使う書き方です。
it { is_expected.to eq(2) }
####use_before_action()
コントローラーで設定したbefore_actionをテスト環境で実行する。
it { is_expected.to use_before_action(:set_article) }
####respond_with()
レスポンスの種類、状態を返す。
is_expected.to respond_with(:redirect)
is_expected.to respond_with 200
is_expected.to respond_with(:success)
####redirect_to()
引数に与えたパスにリダイレクトする。
is_expected.to redirect_to(new_user_session_path)
####set_flash[]
与えた値のフラッシュメッセージが表示されているか確認する。
is_expected.to set_flash[:notice]
####render_with_layout()
描画されているのが引数の指定したものかを確認する。
is_expected.to render_with_layout(:application)
####render_template()
引数に指定したアクションのビューが表示されているか確認。
is_expected.to render_template(:index)
####assigns
コントローラのインスタンス変数をテストするメソッド。
# @articlesをテストしている
expect(assigns[:articles].to_sql).to eq Article.order(id: :desc).limit(25).offset(0).to_sql
####attributes_for
プロジェクトファクトリからテスト用の属性値をハッシュとして作成します。
# 属性のハッシュを生成
alice = attributes_for(:alice)
=> {:name=>"Alice", :admin=>true}
####テスト時のHTTPリクエストの仕方
get :edit, params: { id: article.id }
post :create, params: { article: article_params }
patch :update, params: { id: article.id, article: edit_article_params }
####shared_examples / it_behaves_like
よく使う処理をshared_examplesでまとめて、it_behaves_likeで使うもの。
・
・
・
shared_examples '未ログイン時のテスト' do
it 'result' do
# response
is_expected.to respond_with(:redirect)
is_expected.to redirect_to(new_user_session_path)
# flash
is_expected.to_not set_flash[:notice]
end
end
・
・
・
context '未ログイン時' do
before { get :new }
it_behaves_like '未ログイン時のテスト'
end
####faxtorybotのアソシエーション方法
FactoryBot.define do
factory :comment do
association :article
comment { 'comment' }
end
end
####validate_presence_of
カラムの存在性をテストするもの。
is_expected.to validate_presence_of(:title)
####validate_length_of / is_at_most
カラムの長さを調べる。is_at_mostで最大数を調べる。
is_expected.to validate_length_of(:title).is_at_most(255)
####validate_uniqueness_of
カラムが一意性があるか確認する。
is_expected.to validate_presence_of(:username)
####ルーティングのテストの書き方
it { is_expected.to route(:get, '/').to(controller: 'articles', action: 'index') }
####attr_accessor
クラスやモジュールにインスタンス変数を読み書きするためのアクセサメソッドを定義する。
引数には、インスタンス変数名をシンボルか文字列で指定し複数指定できる。
定義されたメソッドは、シンボルの名前の先頭に「@」を付加したインスタンス変数の値を取り出す。
簡単に言うと、外部からインスタンス変数にアクセスが可能になる。
####shared_context / include_context
context を再利用するためのもの。
・
・
・
shared_context '12歳の場合' do
let(:age) { 12 }
end
・
・
・
context '12歳以下の場合' do
include_context '12歳の場合'
it { is_expected.to eq 'ぼくはU-12だよ。' }
end
####let!
通常のletだと、遅延評価されるが「!」をつけるとexampleの前に実行してくれるようになる。
####pending
なぜかどうしてもテストがパスしないといったテストを、一旦保留にしたいときに使います。
RSpec.describe '謎クラス' do
it '謎テスト' do
expect(1 + 2).to eq 3
pending 'この先はなぜかテストが失敗するので保留'
# パスしないエクスペクテーション(実行される)
expect(foo).to eq bar
end
end
####skip
指定した場所でテストの実行を止めたい場合に使います。
RSpec.describe '実行したくないクラス' do
it '実行したくないテスト' do
expect(1 + 2).to eq 3
skip 'とりあえずここで実行を止める'
# ここから先は実行されない
expect(foo).to eq bar
end
end
####xit
example全体を手っ取り早くskipさせたいときは it を xit に変更するとそのexampleは実行されなくなります。(pending 扱い)
RSpec.describe '何らかの理由で実行したくないクラス' do
xit '実行したくないテスト' do
expect(1 + 2).to eq 3
expect(foo).to eq bar
end
end
####xdescribe / xcontext
describe や context にも x を付けることでスキップできる。
# グループ全体をskipする
xdescribe '四則演算' do
it '1 + 1 は 2 になること' do
expect(1 + 1).to eq 2
end
it '10 - 1 は 9 になること' do
expect(10 - 1).to eq 9
end
end
# グループ全体をskipする
xcontext '管理者の場合' do
it '社員情報を編集できる' do
# ...
end
it '社員情報を削除できる' do
# ...
end
end
##よく使うマッチャ
マッチャ(matcher)は「期待値と実際の値を比較して、一致した(もしくは一致しなかった)という結果を返すオブジェクト」のことです。
以降は良く使うマッチャをいくつか紹介します。
####eq
期待値と実際の値が「等しい」かどうかを検証する
expect(1 + 2).to eq 3
####be
等号・不等号と組み合わせて、値の大小を検証する
expect(1 + 2).to be >= 3
####be_xxx (predicateマッチャ)
戻り値が true / false になるメソッドを検証できる
expect([]).to be_empty?
expect(article).to be_valid?
####be_truthy / be_falsey
「trueっぽい値」または「falseっぽい値」かどうかを検証する。
# 必須項目が入力されていないので保存できない(結果はfalse)
user = User.new
expect(user.save).to be_falsey
# 必須項目が入力されているので保存できる(結果はtrue)
user.name = 'Tanaka'
user.email = 'tanaka@example.com'
expect(user.save).to be_truthy
####change + from / to / by
X すると Y が A から B に変わることを期待することを検証します。
x = [1, 2, 3, 4, 5]
expect{ x.pop }.to change{ x.size }.from(5).to(4)
####include
~が含まれていることを検証する。
x = [1, 2, 3]
expect(x).to include 1
####raise_error
「エラーが起きること」を検証する。
it 'nilを追加するとエラーが発生すること' do
cart = Cart.new
expect{ cart.add nil }.to raise_error 'Item is nil.'
end
####be_within + of
数値 X がプラスマイナス Y の範囲内に収まっていることを検証する。
it '当選確率が約25%になっていること' do
results = Lottery.generate_results(10000)
win_count = results.count(&:win?)
probability = win_count.to_f / 10000 * 100
expect(probability).to be_within(1.0).of(25.0)
end
##RSpecマナーの教え
其の1 : describe の引数にはテストの対象を書く
其の2 : context の引数にはテストが実行される際に前提になる条件や状態を書く
其の3 : describe 外にテストデータを置かない
其の4 : letを上書きしない
其の5 : DRYよりも読みやすさ重視!
##終わりに
ここまでRSpecに役立つ知識やテクニックを詰め込んできましたが、実際に作業しているとよくわからないことばかり出てきますが、分からないことは誰かに調べたり、誰かに聞いたりしてとにかく経験値を積み上げていくのがテストマスターへの近道では無いかと思います!
自分も頑張ろう。。