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. 【WPテーマ自作】ローカル環境でWP開発ができる「Local」の導入
  3. 【Blender】複数バージョンを簡単に管理できる、BlenderLaunche…
  4. 【Blender】MMDファイルをBlenderにImportするアドオン
  5. 【Blender】zip版のBlenderをBlender Launcherに移…

関連記事

 
  1. Ruby on Rails

    【Rails】modelを作成する

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

  2. Ruby on Rails

    【Rails】Factory already registered: エラー

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

  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ファイルでよく使う記述。テーブルにカラムを追加・削…

カレンダー

2023年6月
 1234
567891011
12131415161718
19202122232425
2627282930  

最近の記事

  1. WordPress

    【WPテーマ自作】ローカル環境でWP開発ができる「Local」の導入
  2. Ruby on Rails

    【Rails】railsでIndex name ‘xxx’…
  3. Ruby on Rails

    【Rails】findメソッドで連想配列から指定した値を検索する
  4. Blender

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

    Blender

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