LoginSignup
1
2

More than 3 years have passed since last update.

Rails 6で認証認可入り掲示板APIを構築する #11 userモデルのテストとバリデーション追加

Last updated at Posted at 2020-09-16

Rails 6で認証認可入り掲示板APIを構築する #10 devise_token_auth導入

テストの準備

userにバリデーションとテストを実装します。
テストファーストではなくなってしまっていますが、deviseは特殊なので、ちゃんと動くところまで実装を済ませたかったため、順番が前後しています。

まずはテストに必要なファイルを準備します。
userモデルはdevise_token_authで生成されたため、rspecとfactoryBotのファイルがありません。
以下コマンドで生成します。

$ rails g rspec:model user
      create  spec/models/user_spec.rb
      invoke  factory_bot
      create    spec/factories/users.rb

factoryを先に作ります。

spec/factories/users.rb
# frozen_string_literal: true

FactoryBot.define do
  factory :user do
    provider { "email" }
    sequence(:email) { |n| "test#{n}@example.com" }
    uid { email }
    password { "password" }
    remember_created_at { nil }
    name { "MyString" }
    tokens { nil }
  end
end

userテーブルのカラムは何があったっけ?となったら、db/schema.rbを見ると出力結果が確認できます。

factoryファイルで注目すべきは3点。
sequence(:email) { |n| "test#{n}@example.com" }
uid { email }
password { "password" }

この3つです。

sequence(:email) { |n| "test#{n}@example.com" }
この処理のnには連番が入ってきます。1レコード目はtest1@example.com, 2レコード目はtest2@example.com, …となります。

わざわざこうやっているのは、emailカラムがunique制約がかかっており、同一文字列だと登録できないためです。
例えばここを定数でemail { "test@example.com" }とすると、create_list(:user, 10)で該当メールアドレスが2個以上になってコケちゃうわけですね。

uid { email }
ここはシンプルに、uidにemailの変数を入れているだけです。
deviseの挙動として、providerがemailの場合はuid=emailとなります。

password { "password" }
DBのカラム上はencrypted_passwordですが、そこにはハッシュ化された文字列が保存されます。
パスワード設定時はpasswordに値を入れる必要があるため、schema.rbとカラム名が一致しないのです。

userモデルのテスト

postと似たテストを実行したいので、spec/models/post_spec.rbをコピー。
PostUsersubjectnamebodyemailに置換。

spec/models/user_spec.rb
# frozen_string_literal: true

require "rails_helper"

RSpec.describe User, type: :model do
  describe "name" do
    context "blankの時に" do
      let(:user) do
        build(:user, name: "")
      end
      it "invalidになる" do
        expect(user).not_to be_valid
      end
    end
    context "maxlengthにより" do
      context "30文字の場合に" do
        let(:user) do
          build(:user, name: "あ" * 30)
        end
        it "validになる" do
          expect(user).to be_valid
        end
      end
      context "31文字の場合に" do
        let(:user) do
          build(:user, name: "あ" * 31)
        end
        it "invalidになる" do
          expect(user).not_to be_valid
        end
      end
    end
  end

  describe "email" do
    context "blankの時に" do
      let(:user) do
        build(:user, email: "")
      end
      it "invalidになる" do
        expect(user).not_to be_valid
      end
    end
    context "maxlengthにより" do
      context "100文字の場合に" do
        let(:user) do
          build(:user, email: "@example.com".rjust(100, "a"))
        end
        it "validになる" do
          expect(user).to be_valid
        end
      end
      context "101文字の場合に" do
        let(:user) do
          build(:user, email: "@example.com".rjust(101, "a"))
        end
        it "invalidになる" do
          expect(user).not_to be_valid
        end
      end
    end
    context "email形式により" do
      context "正しい文字列の場合に" do
        let(:user) do
          build(:user, email: "test@example.com")
        end
        it "validになる" do
          expect(user).to be_valid
        end
      end
      context "正しくない文字列の場合に" do
        let(:user) do
          build(:user, email: "test@example")
        end
        it "invalidになる" do
          expect(user).not_to be_valid
        end
      end
    end
  end
end

以下箇所は、postからのコピーではなく独自に置き換えてます。

build(:user, email: "@example.com".rjust(100, "a"))
build(:user, email: "@example.com".rjust(101, "a"))

rjustは文字列が右詰めになるように指定文字で埋めるメソッドです。
rails cで実行してみます。

$ rails c
[1] pry(main)> "@example.com".rjust(100, "a")
=> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@example.com"
[2] pry(main)> "@example.com".rjust(100, "a").length
=> 100

こうすることによって、100文字ピッタリのダミーデータを生成できます。

userモデルのバリデーション実装

app/models/user.rb
 # frozen_string_literal: true

 #
 # ユーザークラス
 #
 class User < ActiveRecord::Base
   # Include default devise modules. Others available are:
   # :confirmable, :lockable, :timeoutable and :omniauthable
   devise :database_authenticatable, :registerable,
          :rememberable, :validatable
   include DeviseTokenAuth::Concerns::User


+  validates :name, presence: true, length: { maximum: 30 }
+  validates :email, presence: true, length: { maximum: 100 }
end

これでrspecのテストを通過するようになります。

あれ?メールアドレスのフォーマットバリデーション書いてないけどなんでOKなの?と感じたら鋭いです。
実はdeviseの:validatableでemailのフォーマットチェックは行われているので、email形式確認バリデーションはdevise依存モデルの場合特に指定不要です。

authのrequest specを書く

今回のチュートリアルの中では、登録とログインだけの簡易的なテストを書いて終わりにします。

$ rails g rspec:request v1/auth
      create  spec/requests/v1/auths_spec.rb
$ mv spec/requests/v1/auths_spec.rb spec/requests/v1/auth_spec.rb

authsが気持ち悪いのでauthにrenameしておきます。
基本的にはspec/requests/v1/posts_spec.rbを参考にしながら、auth_spec.rbを書いていきます。

spec/requests/v1/auth_spec.rb
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "V1::Auth", type: :request do
  describe "POST /v1/auth#create" do
    let(:user) do
      attributes_for(:user, email: "signup@example.com", name: "signupテスト")
    end
    it "正常レスポンスコードが返ってくる" do
      post v1_user_registration_url, params: user
      expect(response.status).to eq 200
    end
    it "1件増えて返ってくる" do
      expect do
        post v1_user_registration_url, params: user
      end.to change { User.count }.by(1)
    end
    it "nameが正しく返ってくる" do
      post v1_user_registration_url, params: user
      json = JSON.parse(response.body)
      expect(json["data"]["name"]).to eq("signupテスト")
    end
    it "不正パラメータの時にerrorsが返ってくる" do
      post v1_user_registration_url, params: {}
      json = JSON.parse(response.body)
      expect(json.key?("errors")).to be true
    end
  end

  describe "POST /v1/auth/sign_in#create" do
    let(:user) do
      create(:user, email: "signin@example.com", name: "signinテスト")
      { email: "signin@example.com", password: "password" }
    end
    it "正常レスポンスコードが返ってくる" do
      post v1_user_session_url, params: user, as: :json
      expect(response.status).to eq 200
    end
    it "nameが正しく返ってくる" do
      post v1_user_session_url, params: user
      json = JSON.parse(response.body)
      expect(json["data"]["name"]).to eq("signinテスト")
    end
    it "不正パラメータの時にerrorsが返ってくる" do
      post v1_user_session_url, params: {}
      json = JSON.parse(response.body)
      expect(json.key?("errors")).to be true
    end
  end
end

ここまで書いて、rubocopとrspecが正常に通ればcommitします。

続き

Rails 6で認証認可入り掲示板APIを構築する #12 userとpostの関連付け
連載目次へ

1
2
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
1
2