fv17の日記 - Coding Every Day

Webエンジニアの備忘用ブログです。主にWeb界隈の技術に関して書いています。

【各章まとめ】Everyday Rails - RSpecによるRailsテスト入門 - 第7章 リクエストスペック

Everyday RailsRSpecによるRailsテスト入門 - 第7章 リクエストスペック」の復習用まとめ。
letなどは第7章でまだ出てきていないため、DRYではない箇所多数。

目次

  • (APIの)GETリクエストのテスト
  • (APIの)POSTリクエストのテスト
  • コントローラスペックをリクエストスペックで置き換える

GETリクエストのテスト

リクエストスペックの作成

bin/rails g rspec:request projects_api

ファイル名称を変更する

自動生成される spec/requests/projects_apis_spec.rb だとおかしいので、projects_api_spec.rb に変更

テスト実装

RSpec.describe "Projects API", type: :request do
  it "loads a project" do
    user = FactoryBot.create(:user)
    FactoryBot.create(:project, name: "My Project", owner: user)
    FactoryBot.create(:project, name: "Other user Project")

    # index アクションのテスト
    get api_projects_path, params: {
        user_email: user.email,
        user_token: user.authentication_token
    }

    expect(response).to have_http_status(:success)
    json = JSON.parse(response.body)
    expect(json.length).to eq 1

    # show アクションのテスト
    project_id = json[0]["id"]
    get api_project_path(project_id), params: {
        user_email: user.email,
        user_token: user.authentication_token
    }

    expect(response).to have_http_status(:success)
    json = JSON.parse(response.body)
    expect(json["name"]).to eq "My Project"
  end
end

POSTリクエストのテスト

RSpec.describe "Projects API", type: :request do
  
  # index、showのテストは略

  it "create a project" do
    user = FactoryBot.create(:user)
    project_params = FactoryBot.attributes_for(:project, owner: user)

    expect do
      post api_projects_path, params: {
          user_email: user.email,
          user_token: user.authentication_token,
          project: project_params
      }
    end.to change(user.projects, :count).by(1)

    expect(response).to have_http_status(:success)
  end
end

コントローラスペックをリクエストスペックで置き換える

indexのテスト

RSpec.describe "Home Page", type: :request do
  it "responds successfully" do
    get root_path  # ここはいい感じに読み替える
    expect(response).to be_success
    expect(response).to have_http_status "200"
  end
end

createのテスト

  • 基本的にコントローラスペックとほぼ同一。
  • POSTリクエストの送信方法が post :create から post projects_path と名前付きに変わる。
  • Deviceで認証制御している場合は、request spec内で認証用メソッドを呼ぶ設定が必要。
require 'rails_helper'

RSpec.describe "Projects", type: :request do
  describe "#create" do
    context "ログインしている場合" do
      let(:user) { FactoryBot.create(:user) }

      context "有効な属性値の場合" do
        let(:project_params) { FactoryBot.attributes_for(:project) }

        it "プロジェクトを追加できる" do
          sign_in user
          expect {
            post projects_path, params: { project: project_params }
          }.to change(user.projects, :count).by(1)
        end
      end

      context "無効な属性値の場合" do
        let(:invalid_project_params) { FactoryBot.attributes_for(:project, :invalid) }

        it "プロジェクトを追加できない" do
          sign_in user
          expect {
            post projects_path, params: { project: invalid_project_params }
          }.not_to change(user.projects, :count)
        end
      end
    end

    context "ログインしているが、プロジェクトのオーナーではない場合" do
      # ...
    end

    context "ログインしていない場合" do
      # ...
    end
  end
end

request spec内で認証用メソッドを呼ぶ設定をしないとダメ。

公式、もしくは書籍参照。everyday Railsでは簡単な方ではなく、詳細な方の設定をしている。
How To: sign in and out a user in Request type specs (specs tagged with type: :request) · plataformatec/devise Wiki · GitHub

念のためここにも記述。

spec/support/request_spec_helper.rbを作成
module RequestSpecHelper include Warden::Test::Helpers
  def self.included(base)
    base.before(:each) { Warden.test_mode! }
    base.after(:each) { Warden.test_reset! }
  end

  def sign_in(resource)
    login_as(resource, scope: warden_scope(resource))
  end

  def sign_out(resource)
    logout(warden_scope(resource))
  end

  private
  def warden_scope(resource)
    resource.class.name.underscore.to_sym
  end
end

rails_helper.rb

RSpec.configure do |config|
  # ...
  # Use Devise helpers in tests
  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include RequestSpecHelper, type: :request  # ←こいつを追加する
end