【Rails】RSpecを使った自動テスト《コントローラースペック編》

はじめに

Railsでアプリケーションを開発する際、品質を担保するためにテストは欠かせません。その中でも、RSpecは多くの開発者に愛用されているテストフレームワークです。

今回は、RSpecを使ったRailsアプリケーションのテスト方法について、実践的な視点から解説していきます。

コントローラースペック

アクションのテスト

コントローラーのアクションのテストでは、リクエストが正しく処理され、適切なレスポンスやビューが返されるかを確認します。以下に、典型的なコントローラのアクションテストの例を示します。

まず、UsersControllerを定義します。

app/controllers/users_controller.rb

class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new
    end
  end

  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
      redirect_to @user, notice: 'User was successfully updated.'
    else
      render :edit
    end
  end

  def destroy
    @user = User.find(params[:id])
    @user.destroy
    redirect_to users_url, notice: 'User was successfully destroyed.'
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :password)
  end
end

次に、RSpecを使ってこれらのアクションをテストします。

spec/controllers/users_controller_spec.rb

require 'rails_helper'

RSpec.describe UsersController, type: :controller do
  let(:valid_attributes) { { name: 'Alice', email: 'alice@example.com', password: 'password' } }
  let(:invalid_attributes) { { name: nil, email: 'alice@example.com', password: 'password' } }

  describe 'GET #index' do
    it 'returns a success response' do
      User.create!(valid_attributes)
      get :index
      expect(response).to be_successful
      expect(assigns(:users)).to eq(User.all)
    end
  end

  describe 'GET #show' do
    it 'returns a success response' do
      user = User.create!(valid_attributes)
      get :show, params: { id: user.to_param }
      expect(response).to be_successful
      expect(assigns(:user)).to eq(user)
    end
  end

  describe 'GET #new' do
    it 'returns a success response' do
      get :new
      expect(response).to be_successful
      expect(assigns(:user)).to be_a_new(User)
    end
  end

  describe 'POST #create' do
    context 'with valid params' do
      it 'creates a new User' do
        expect {
          post :create, params: { user: valid_attributes }
        }.to change(User, :count).by(1)
      end

      it 'redirects to the created user' do
        post :create, params: { user: valid_attributes }
        expect(response).to redirect_to(User.last)
        expect(flash[:notice]).to eq('User was successfully created.')
      end
    end

    context 'with invalid params' do
      it 'does not create a new User' do
        expect {
          post :create, params: { user: invalid_attributes }
        }.not_to change(User, :count)
      end

      it 'renders the new template' do
        post :create, params: { user: invalid_attributes }
        expect(response).to render_template(:new)
      end
    end
  end

  describe 'GET #edit' do
    it 'returns a success response' do
      user = User.create!(valid_attributes)
      get :edit, params: { id: user.to_param }
      expect(response).to be_successful
      expect(assigns(:user)).to eq(user)
    end
  end

  describe 'PATCH #update' do
    context 'with valid params' do
      let(:new_attributes) { { name: 'Bob' } }

      it 'updates the requested user' do
        user = User.create!(valid_attributes)
        patch :update, params: { id: user.to_param, user: new_attributes }
        user.reload
        expect(user.name).to eq('Bob')
      end

      it 'redirects to the user' do
        user = User.create!(valid_attributes)
        patch :update, params: { id: user.to_param, user: new_attributes }
        expect(response).to redirect_to(user)
        expect(flash[:notice]).to eq('User was successfully updated.')
      end
    end

    context 'with invalid params' do
      it 'does not update the user' do
        user = User.create!(valid_attributes)
        patch :update, params: { id: user.to_param, user: invalid_attributes }
        expect(response).to render_template(:edit)
      end
    end
  end

  describe 'DELETE #destroy' do
    it 'destroys the requested user' do
      user = User.create!(valid_attributes)
      expect {
        delete :destroy, params: { id: user.to_param }
      }.to change(User, :count).by(-1)
    end

    it 'redirects to the users list' do
      user = User.create!(valid_attributes)
      delete :destroy, params: { id: user.to_param }
      expect(response).to redirect_to(users_url)
      expect(flash[:notice]).to eq('User was successfully destroyed.')
    end
  end
end
  1. GET #index
    • get :indexを実行し、レスポンスが成功(200 OK)であることを確認します。
    • @usersインスタンス変数に全てのユーザーが正しく割り当てられていることを確認します。
  2. GET #show
    • ユーザーが存在する場合、get :show, params: { id: user.to_param }を実行し、レスポンスが成功(200 OK)であることを確認します。
    • @userインスタンス変数に該当するユーザーが正しく割り当てられていることを確認します。
    • ユーザーが存在しない場合、レスポンスが404 Not Foundであることを確認します。
  3. GET #new
    • get :newを実行し、レスポンスが成功(200 OK)であることを確認します。
    • @userインスタンス変数に新しいユーザーオブジェクトが割り当てられていることを確認します。
  4. POST #create
    1. 有効なパラメータの場合 (with valid params)
      • 新しいユーザーが作成されることを確認します。
      • 作成後、ユーザーの詳細ページにリダイレクトされることを確認します。
    2. 無効なパラメータの場合 (with invalid params)
      • 新しいユーザーが作成されないことを確認します。
      • 新規作成ページが再表示されることを確認します。
  5. GET #edit
    • get :edit, params: { id: user.to_param }を実行し、レスポンスが成功(200 OK)であることを確認します。
    • @userインスタンス変数に該当するユーザーが正しく割り当てられていることを確認します。
  6. PATCH #update
    1. 有効なパラメータの場合 (with valid params)
      • ユーザーが正しく更新されることを確認します。
      • 更新後、ユーザーの詳細ページにリダイレクトされることを確認します。
    2. 無効なパラメータの場合 (with invalid params)
      • ユーザーが更新されないことを確認します。
      • 編集ページが再表示されることを確認します。
  7. DELETE #destroy
    • delete :destroy, params: { id: user.to_param }を実行し、該当するユーザーが削除されることを確認します。
    • 削除後、ユーザーリストページにリダイレクトされることを確認します。

まとめ

RSpecを使いこなすことで、Railsアプリケーションの品質を大幅に向上させることができます。ただし、テストの書きすぎには注意が必要です。重要な機能や複雑なロジックに焦点を当て、バランスの取れたテスト戦略を立てることが大切です。

また、CIツールと組み合わせることで、継続的にテストを実行し、問題を早期に発見することができます。例えば、GitHubActionsを使えば、プッシュやプルリクエスト時に自動的にテストを実行できます。

Railsアプリケーション開発において、RSpecは強力な味方となります。ぜひ、日々の開発に取り入れて、より堅牢なアプリケーション作りを目指してください。

関連記事

【Rails】Paranoiaを使用した論理削除(ソフトデリート)
# はじめに Paranoiaは、Railsアプリケーションで論理削除(ソフトデリート)を実現するためのGemです。 論理削除は、データベースのレコードを物理的に削除するのではなく、削除フラグを設定することで「削除済み」とみなす方法です。こ [...]
2024年7月20日 21:33
【Rails】activerecord-multi-tenantを使用したマルチテナントアプリケーションの作成
# はじめに マルチテナントアプリケーションでは、複数の顧客(テナント)が同じアプリケーションを利用するため、データの分離が必要です。 activerecord-multi-tenantは、このようなマルチテナント環境をサポートするための便 [...]
2024年7月18日 16:50
【Rails】RubyとRailsにおけるattr_reader, attr_writer, attr_accessorの概念と使用方法
# はじめに RubyとRailsの開発において、`attr_reader`,`attr_writer`,`attr_accessor`は非常に便利なメソッドです。これらは、クラス内でインスタンス変数に対するゲッターおよびセッターメソッドを簡単に [...]
2024年7月17日 18:11
【Rails】RubyとRailsにおけるyieldの概念と使用方法
# はじめに RubyとRailsにおける`yield`は、メソッドやテンプレートの中で動的にコードブロックを実行する能力を提供し、これによってコードの再利用性と拡張性が大幅に向上します。本記事では、RubyとRailsにおける`yield`の概 [...]
2024年7月17日 13:15
【Rails】AASMを使用してオブジェクトの状態遷移を効率的に管理
# はじめに Railsアプリケーションにおいて、オブジェクトの状態管理は重要な課題の一つです。AASM (Acts As State Machine) gemは、複雑な状態遷移を効率的に管理します。本記事では、AASMの基本的な使い方を解説して [...]
2024年7月16日 18:00
【Rails】RSpec + Swagger + rswagでアプリケーションのAPIをテストおよびドキュメント化する方法
# はじめに Railsアプリケーションの開発において、APIのテストとドキュメント化は重要な要素です。 RSpecはテストフレームワークとして広く利用されており、SwaggerはAPIの設計とドキュメント化を支援します。これらを統合するr [...]
2024年7月16日 14:27
【Rails】mailcatcherを使用して開発環境でメール送信をテストする方法
# はじめに mailcatcherは、開発環境でのメール送信をキャプチャするためのツールです。ローカルで送信されたメールをブラウザ上で簡単に確認できるようにします。mailcatcherをRailsアプリケーションで使用する方法について説明しま [...]
2024年7月15日 16:37
【Rails】impressionistを使用してページビューやクリック数を追跡する方法
# はじめに impressionist Gemを使用してRailsアプリケーションでページビューやクリック数を追跡する方法について説明します。 # 実装方法 ## impressionist Gemのインストール まず、impre [...]
2024年7月15日 14:18