26
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails API】devise_token_auth実装〜Rspecテストまで【Rspecテスト編】

Last updated at Posted at 2019-05-10

いよいよ大事なテストを書いていきます。

この記事では、テスト項目について書きます。

Rspecの基礎的な書き方については対象外とします。

こちらの記事でdeviceを実装した前提です。

記事の完成データはこちら
https://github.com/daisuke131/devise_token_auth_rspec

必要なファイルを作成

$ bundle exec rails g rspec:model user

これにより、spec/models/user_spec.rbspec/factories/users.rbが生成されます。

続いてspec/requests/api/v1/auth/registrations_spec.rbを作成します

これでファイルが揃いました。

factoriesファイル修正

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name { Faker::Name.name }
    sequence(:email) { |n| "#{n}_" + Faker::Internet.email }
    password { Faker::Internet.password(8) }
  end
end

emailは一意テストをするので重複しないよう念の為、emailの最初に番号をつけてます。

モデルのテスト

テスト項目

ここではUserモデルの保存処理のテストを行います。

  • 全カラムの値を指定してるとき、レコードが作成されること
  • emailを指定していないとき、エラーになること
  • passwordを指定していないとき、エラーになること
  • すでに保存されているemailを指定したとき、エラーになること
spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe "validates presence" do
    context "全カラムの値を指定しているとき" do
      let(:user) { create(:user) }

      it "userのレコードが作成される" do
        expect(user).to be_valid
      end
    end

    context "emailを指定していないとき" do
      let(:user) { build(:user, email: nil) }

      it "エラーになる" do
        user.valid?
        expect(user.errors.messages[:email]).to include "can't be blank"
      end
    end

    context "passwordを指定していないとき" do
      let(:user) { build(:user, password: nil) }

      it "エラーになる" do
        user.valid?
        expect(user.errors.messages[:password]).to include "can't be blank"
      end
    end
  end

  describe "validates uniqueness" do
    context "保存されたメールアドレスが指定されたとき" do
      let(:user1) { create(:user) }
      let(:user2) { build(:user, email: user1.email) }

      it "エラーになる" do
        user2.valid?
        expect(user2.errors.messages[:email]).to include "has already been taken"
      end
    end
  end
end

devise_token_authでは、デフォルトでvalidates :email, :password, presence: truevalidates :email, uniqueness: trueが掛かっているのでこのようなテスト項目になりました。
仕様に応じてnameをpresence: trueにしてテストしてみたりしてください。
同じように書けばテスト通ります。

コントローラのテスト

テスト項目

ここではコントローラの処理のテストを行います。
routes.rbで指定されているルートに沿ってテストします。
今回コントローラー側でpassword_confirmをユーザー登録の条件に含めているのでそれもテストしていきます。

  • ユーザー情報保存処理
    • ユーザーが登録できること
      • response.body["status"] = "success"
      • response.body["data"]["id"] = 登録したユーザーid
      • response.body["data"]["email"] = 登録したユーザーemail
      • response.status = 200
    • password_confirmationの値がpasswordと異なるときユーザー登録できない。
      • password_confirmationを空白で登録しようとする
      • response.body["status"] = "error"
      • エラーメッセージに"Password confirmation doesn't match Password"が含まれる。
      • response.status = 422
  • ログイン処理
    • email、passwordが正しいとき、ログインできること
      • response.headers["uid"]が存在する
      • response.headers["access-token"]が存在する
      • response.headers["client"]が存在する
      • response.status = 200
    • emailが正しくない場合、ログインできないこと
      • response.body["success"] = false
      • response.body["errors"]が想定したエラー文と等しい
      • response.headers["uid"]が空
      • response.headers["access-token"]が空
      • response.headers["client"]が空
      • response.status = 401
    • passwordが正しくない場合、ログインできないこと
      • response.body["success"] = false
      • response.body["errors"]が想定したエラー文と等しい
      • response.headers["uid"]が空
      • response.headers["access-token"]が空
      • response.headers["client"]が空
      • response.status = 401
  • ログアウト処理
    • ログインしているとき、ログアウトできること
      • response.body["success"] = true
      • カレントユーザーのreload.tokensが空であること
      • response.status = 200
spec/requests/api/v1/auth/registrations_spec.rb
require "rails_helper"

RSpec.describe "Api::V1::Auth::Registrations", type: :request do
  describe "POST /api/v1/auth" do
    subject { post(api_v1_user_registration_path, params: params) }
    context "値が正しく入力されているとき" do
      let(:params) { attributes_for(:user) }
      it "ユーザー登録できる" do
        subject
        res = JSON.parse(response.body)
        expect(res["status"]).to eq("success")
        expect(res["data"]["id"]).to eq(User.last.id)
        expect(res["data"]["email"]).to eq(User.last.email)
        expect(response).to have_http_status(200)
      end
    end

    context "password_confirmationの値がpasswordと異なるとき" do
      let(:params) { attributes_for(:user, password_confirmation: "") }
      it "ユーザー登録できない" do
        subject
        res = JSON.parse(response.body)
        expect(res["status"]).to eq("error")
        expect(res["errors"]["full_messages"]).to include("Password confirmation doesn't match Password")
        expect(response).to have_http_status(422)
      end
    end
  end

  describe "POST /api/v1/auth/sign_in" do
    subject { post(api_v1_user_session_path, params: params) }
    context "メールアドレス、パスワードが正しいとき" do
      let(:current_user) { create(:user) }
      let(:params) { { email: current_user.email, password: current_user.password } }
      it "ログインできる" do
        subject
        expect(response.headers["uid"]).to be_present
        expect(response.headers["access-token"]).to be_present
        expect(response.headers["client"]).to be_present
        expect(response).to have_http_status(200)
      end
    end

    context "メールアドレスが正しくないとき" do
      let(:current_user) { create(:user) }
      let(:params) { { email: "test@example.com", password: current_user.password } }
      it "ログインできない" do
        subject
        res = JSON.parse(response.body)
        expect(res["success"]).to be_falsey
        expect(res["errors"]).to include("Invalid login credentials. Please try again.")
        expect(response.headers["uid"]).to be_blank
        expect(response.headers["access-token"]).to be_blank
        expect(response.headers["client"]).to be_blank
        expect(response).to have_http_status(401)
      end
    end

    context "パスワードが正しくないとき" do
      let(:current_user) { create(:user) }
      let(:params) { { email: current_user.email, password: "password" } }
      it "ログインできない" do
        subject
        res = JSON.parse(response.body)
        expect(res["success"]).to be_falsey
        expect(res["errors"]).to include("Invalid login credentials. Please try again.")
        expect(response.headers["uid"]).to be_blank
        expect(response.headers["access-token"]).to be_blank
        expect(response.headers["client"]).to be_blank
        expect(response).to have_http_status(401)
      end
    end
  end

  describe "DELETE /api/v1/auth/sign_out" do
    subject { delete(destroy_api_v1_user_session_path, headers: headers) }
    context "ユーザーがログインしているとき" do
      let(:current_user) { create(:user) }
      let(:headers) { current_user.create_new_auth_token }
      fit "ログアウトできる" do
        subject
        res = JSON.parse(response.body)
        expect(res["success"]).to be_truthy
        expect(current_user.reload.tokens).to be_blank
        expect(response).to have_http_status(200)
      end
    end
  end
end

こんな感じで書いてみました。

おわり

devise_token_auth実装〜Rspecテストを3記事に分けて書きました。

devise実装編

外部ツールで確認編

テスト項目がまだ未熟かもしれません。

気になった方は教えてください!

26
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?