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. 【C97】冬コミ受かってました
  2. 【Rails】オブジェクトの中身をログに出力する
  3. 【git】git checkoutする時に、ベースブランチを指定する
  4. 【WPテーマ自作】UnderScoresの導入
  5. 【C97】冬コミ申し込みました!

関連記事

  1. Ruby on Rails

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

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

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

    Ruby on Rails

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

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

  3. Ruby on Rails

    [Rails]findメソッドで連想配列から指定した値を検索する

    目次findメソッドとは基本的な使い方selectとの違い連…

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

    Ruby on Rails

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

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

  5. Ruby on Rails

    【Rails】modelを作成する

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

カレンダー

2021年4月
 1234
567891011
12131415161718
19202122232425
2627282930  

最近の記事

  1. Ruby on Rails

    【Rails】ActiveStorageで添付ファイルを削除したいときは、pur…
  2. git logで変更したファイル名を確認したい

    git

    【git】git logで変更ファイル名を確認したい!
  3. 同人イベント

    【C97】冬コミ申し込みました!
  4. 【WPテーマ自作】UnderScoresの導入

    WordPress

    【WPテーマ自作】UnderScoresの導入
  5. 同人イベント

    【C98】夏コミ申し込みました。
PAGE TOP