Ruby on Rails

【Rails】RSpecのお作法メモ

RSpecを書く機会が増えてきたので、記法の備忘録として記事にします。

describe ~ it

テストしたいアクションをdescribeで囲みます。
controllerのupdateアクションのspecを書く場合
describe ‘#update’ のように、アクション単位でテストを書いていきます。

# some_logics_controller_spec.rb

describe 'アクション名' do
  # テストしたいアクションをここに定義する
  subject { get :index }

  it 'indexテンプレートで描画されること' do
    # subjectを呼び出すと、indexアクションが実行される
    subject
    expect(response).to render_template('index')
  end
end

let、let!

リクエストを送る際に必要なパラメータや
テスト中に変数を使いたい場合は、let を使用します。

let は遅延評価され、let! は即時評価されます。
つまり、subjectを実行する前にアクセスする必要がある変数は
let! で定義しておく必要があります。

例えば以下の例では
letで定義しているuser、parametersは
it ‘how_test_context’ブロック内で、subjectが実行された時に評価されます。

# some_logics_controller_spec.rb

describe 'アクション名' do
  subject { patch :update, params: parameters }

  # let(:variable_name) で変数を定義できる
  ## let は変数が呼ばれたタイミングで評価される
  let(:user) { create(:user) }
  let(:parameters) { { user_id: user.id, name: 'ミミッキュ' } }

  ## let! はsubjectが実行される前に評価される
  let!(:item) { create(:item) }

  ## user_itemはsubjectの後に評価されるので、エラーになる
  let(:user_item) { create(:user_item, user_id: user.id, item_id: item.id) }

  it 'userがitemを持っていること' do
    subject
    expect(user.user_items.first).to eq user_item
  end
end

対して、userとitemを紐づけるためのuser_itemは
letで定義していますが、テスト内で呼び出されるのはsubjectが実行された後なので、このspecはエラーとなってしまいます。

context ‘some_test’ do

シンプルなテストであれば、describeとitだけで事足りますが
より複雑な条件でテストを書きたい時もあります。というか、ほとんどがそうだと思います。
そういう時は、context ‘some_pattern’ do で囲います。

次の例では、無効なユーザーのテストをcontextで囲み
before doで、テストが実行される前にuserのステータスをinactiveに変更しています。
(before doについては、次の節で触れていきます)

# some_logics_controller_spec.rb

describe 'アクション名' do
  subject { post :login, params: { user: user } }
  let(:user) { create(:user, status: :active) }

  # 正常系の処理
  it 'ログインできること' do
    subject
    expect(response.status).to eq 201
  end

  # context 'pattern_name' do で、特定の条件下での処理を囲んであげる
  context '無効なユーザーの場合' do
    before do
      user.inactive!
    end

    it 'ログインできないこと' do
      subject
      expect(response.status).to eq 403
    end
  end
end

ログイン機能のテストを書く時など
テストを書き分けたい場合に便利です。

before_action

railsでは、特定のアクションを呼び出す前に実行する処理(before_action)を定義できます。
rspecでも同じようなことができて
before do ブロックで処理を囲んであげることで、ブロック内の処理を
itが実行される直前に実行してくれます。

先ほどのログイン機能を例に、以下のコードを見てみます。
あらかじめ無効なユーザーをlet変数で定義しておき、
beforeアクションでユーザーをアクティベートさせてから、subject(ログイン)を実行しています。

# some_logics_controller_spec.rb

describe 'アクション名' do
  subject { post :login, params: { user: user } }
  # 無効なユーザー
  let(:user) { create(:user, status: :inactive) }

  # before do で囲んだ処理は、itの直前に毎回実行される
  before do
    user.active!
  end

  # before doの処理が実行される
  it 'ログインできること' do
    subject
    expect(response.status).to eq 201
  end

  # before doの処理が実行される
  it 'ログインできないこと' do
    subject
    expect(response.status).to eq 403
  end
end

しかし、beforeアクションは、itの直前に毎回実行されるので
it ‘ログインできないこと’ do の部分でエラーになります。

before(:context)

beforeブロックは、デフォルトでは itが実行される直前に毎回呼ばれます。

先ほどの例で言えば
it ‘ログインできないこと’ do の直前にbeforeが実行される時
userはすでにアクティベーションされている状態になります。
(it ‘ログインできること’ do の直前にもbeforeが実行されるため)

itの前に、一度だけ実行すれば良い場合は
before(:context) を使用します。
before(:context) は、describeや、contextのブロックごとに毎回実行されます。

# some_logics_controller_spec.rb

describe 'アクション名' do
  subject { post :login, params: { user: user } }
  # 無効なユーザー
  let(:user) { create(:user, status: :inactive) }

  # before(:context) はdescribe、contextのグループごとに実行される
  before(:context) do
    user.active!
  end

  it 'ログインできること' do
    subject
    expect(response.status).to eq 201
  end

  it 'ログインできないこと' do
    subject
    expect(response.status).to eq 403
  end
end

before(:context)は、before(:all)のエイリアスです。
何も指定しない場合、beforeのデフォルトは、before(:each)になり
before(:each)は、itの直前に毎回実行されます。

ピックアップ記事

  1. 【Rails】railsでIndex name ‘xxx’…
  2. 【Blender】起動時に強制終了してしまう問題
  3. 【Blender】レンダリング結果を新規ウィンドウで開かないようにする
  4. 【Rails】findメソッドで連想配列から指定した値を検索する
  5. 【Blender】Bumpを使用した質感表現の方法

関連記事

 
  1. Ruby on Rails

    【Rails】Factory already registered: エラー

    railsでアプリ開発中、最新のfeatureブランチでリベースして…

  2. Ruby on Rails

    【Rails】railsでIndex name ‘xxx’ on table…

    railsで、dbを変更するためにmigrationを実行した際、in…

  3. Ruby on Rails

    【Rails】レコードが保存済みか確認するpersisted?メソッド

    レコードが保存されているかどうかでUIの表示を切り替えたり、保存されて…

  4. railsをproductionモードで実行する

    Ruby on Rails

    【Rails】railsをproductionモードで実行する

    結論--environmentオプションを使用します。…

  5. Ruby on Rails

    【Rails】selectメソッドで特定の条件を満たす要素を取得する

    selectは、配列に対してブロック内の条件を評価し、真になって要素を…

カレンダー

2024年10月
 123456
78910111213
14151617181920
21222324252627
28293031  

最近の記事

  1. gitでファイル変更の一部をコミットする

    git

    git add -p でファイルの一部をコミットする【Git】
  2. Ruby on Rails

    【Rails】modelを作成する
  3. Blender

    【Blender】拡張機能(アドオン)の基本的な使い方
  4. Ruby on Rails

    【Rails】selectメソッドで特定の条件を満たす要素を取得する
  5. Ruby on Rails

    【Rails】railsでIndex name ‘xxx’…
PAGE TOP