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. 【Rails】findメソッドで連想配列から指定した値を検索する
  3. 【Blender】Bumpを使用した質感表現の方法
  4. 【Rails】selectメソッドで特定の条件を満たす要素を取得する
  5. 【Rails】modelを作成する

関連記事

 
  1. Ruby on Rails

    【Rails】ActiveStorageで添付ファイルを削除したいときは、purgeを使う

    動作確認するために、active_storageでアップロードした画…

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

    Ruby on Rails

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

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

  3. 【Rails】オブジェクトの中身をログに出力する

    Ruby on Rails

    【Rails】オブジェクトの中身をログに出力する

    オブジェクトの中身をデバッグしたりする時に便利出力結果をみやすい形に…

  4. Ruby on Rails

    【Rails】development? production? 開発環境ごとに処理を切り分けたい時

    Railsで、開発環境ごとに処理を切り替えたい時の方法を紹介します。ま…

  5. Ruby on Rails

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

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

  6. Ruby on Rails

    【Rails】migrationでカラムを追加・削除したい

    migrationファイルでよく使う記述。テーブルにカラムを追加・削…

カレンダー

2021年12月
 12345
6789101112
13141516171819
20212223242526
2728293031  

最近の記事

  1. 慣れれば3分!Bumpノードを使用した質感表現の方法

    Blender

    【Blender】Bumpを使用した質感表現の方法
  2. Blender

    【UE4】用語備忘録メモ
  3. MySQL

    【MySQL】mysqlで新しくユーザーを追加する
  4. Blenderの複数バージョンを簡単に管理できるBlenderLauncherの使い方

    Blender

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

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