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. 【Blender】起動時に強制終了してしまう問題
  2. 【ツール開発】ポケモンの種族値と実数値を調べるツール – 1
  3. 【Rails】オブジェクトの中身をログに出力する
  4. 【Blender】複数バージョンを簡単に管理できる、BlenderLaunche…
  5. 【Blender】アニメーションでポーズを左右反転してコピペしたい時

関連記事

 
  1. Ruby on Rails

    【Rails】文字列からHTMLタグを取り除く方法

    Railsで、HTMLタグを取り除いて文字列を出力したい時があります。…

  2. Ruby on Rails

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

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

  3. Ruby on Rails

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

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

  4. Ruby on Rails

    【Rails】modelを作成する

    結論rails generateコマンドを使いましょ…

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

    Ruby on Rails

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

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

カレンダー

2021年8月
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

最近の記事

  1. Blender

    【UE4】用語備忘録メモ
  2. Ruby on Rails

    【Rails】development? production? 開発環境ごとに処…
  3. Blender

    【Blender】アニメーションでポーズを左右反転してコピペしたい時
  4. Blender

    【Blender】起動時に強制終了してしまう問題
  5. Blenderの複数バージョンを簡単に管理できるBlenderLauncherの使い方

    Blender

    【Blender】複数バージョンを簡単に管理できる、BlenderLaunche…
PAGE TOP