1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

投稿アプリのモデルの単体テストコード

Posted at

テストコードは本当に理解できず一周目は正直飛ばしてましたが
復習してなんとなく理解できました!
というわけで復習の備忘録です!

目的

  • Userモデルの単体テストの実装を通して、正常系テストを理解
  • Userモデルの単体テストの問題を解いて、異常系テストに関する知識を定着
  • Tweetモデルの単体テストの問題を解いて、モデル単体テストに関する知識を定着

####文字数制限のバリデーションを設置
「保存できる値は最大6文字まで」というバリデーションを設置
app/models/user.rb

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :validatable
  has_many :tweets
  has_many :comments  # commentsテーブルとのアソシエーション

  validates :nickname, presence: true, length: { maximum: 6 }
end

フォームに設けている文字数制限を削除

app/views/devise/registrations/new.html.erb

<div class="contents row">
  <div class="container">
    <h2>Sign up</h2>
    <%= form_with model: @user, url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>
      <%= render "devise/shared/error_messages", resource: resource %>

      <div class="field">
        <%= f.label :nickname %> <em>(6 characters maximum)</em><br />
        <%= f.text_field :nickname, autofocus: true %>
      </div>

<%# 以下省略 %>

Userのモデル単体テストの実装

exampleを整理

モデル単体テストはバリデーションやメソッドの検証です
バリデーションは「属性の値がDBに保存されても良いかチェックを行う条件」を設定
presenceは、指定した属性の値の有無をチェック
Userモデルでは、nicknameに対して、presence:trueの条件を指定しています
この場合、DBに保存する前にnicknameに値が存在していなければ、条件に引っ掛かりDBに保存されません
length は、指定した属性の値の文字数制限をチェックします
Userモデルでは、nicknameに対して、length: { maximum: 6 } の条件を指定しています。

  • nicknameとemail、passwordとpassword_confirmationが存在すれば登録できる
  • nicknameが空では登録できない
  • emailが空では登録できない
  • passwordが空では登録できない
  • passwordとpassword_confirmationが不一致では登録できない
  • nicknameが7文字以上では登録できない
  • 重複したemailが存在する場合は登録できない
  • emailは@を含まないと登録できない
  • passwordが5文字以下では登録できない
  • passwordが129文字以上では登録できない

新規登録できる場合のテストコードを記述

  1. 検証したいモデルのインスタンスを生成
  2. 生成したインスタンスがどのような状況であればいいか記述

be_valid

valid?のメソッドの返り値が、trueであることを期待するマッチャ

% rails c
[1] pry(main)> @user = FactoryBot.build(:user)
  (4.3ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
=> #<User id: nil, email: "judie@gmail.com", created_at: nil, updated_at: nil, nickname: "GP">
[2] pry(main)> @user.valid?
  User Exists? (10.3ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'judie@gmail.com' LIMIT 1
=> true

spec/models/user_spec.rb

it 'nicknameとemail、passwordとpassword_confirmationが存在すれば登録できる' do
  expect(@user).to be_valid
end

テストコード
bundle exec rspec spec/models/user_spec.rb

新規登録できない場合のテストコードを記述

spec/models/user_spec.rb

it 'nicknameが空で登録できない' do
      @user.nickname = ''
      @user.valid?
      expect(@user.errors.full_messages).to include("Nickname can't be blank")
    end
    it 'emailが空で登録できない' do
      @user.email = ''
      @user.valid?
      expect(@user.errors.full_messages).to include("Email can't be blank")
    end
    it 'passwordが空では登録できない' do
      @user.password = ''
      @user.valid?
      expect(@user.errors.full_messages).to include("Password can't be blank")
    end
    it 'passwordとpassword_confirmationが不一致では登録できない' do
      @user.password = '123456'
      @user.password_confirmation = '1234567'
      @user.valid?
      expect(@user.errors.full_messages).to include("Password confirmation doesn't match Password")
    end
    it 'nicknameが7文字以上では登録できない' do
      @user.nickname = 'aaaaaaa'
      @user.valid?
      expect(@user.errors.full_messages).to include('Nickname is too long (maximum is 6 characters)')
    end
    it '重複したemailが存在する場合は登録できない' do
      @user.save
      another_user = FactoryBot.build(:user)
      another_user.email = @user.email
      another_user.valid?
      expect(another_user.errors.full_messages).to include('Email has already been taken')
    end
    it 'emailは@を含まないと登録できない' do
      @user.email = 'testmail'
      @user.valid?
      expect(@user.errors.full_messages).to include('Email is invalid')
    end
    it 'passwordが5文字以下では登録できない' do
      @user.password = '00000'
      @user.password_confirmation = '00000'
      @user.valid?
      expect(@user.errors.full_messages).to include('Password is too short (minimum is 6 characters)')
    end
    it 'passwordが129文字以上では登録できない' do
      @user.password = Faker::Internet.password(min_length: 129, max_length: 150)
      @user.password_confirmation = @user.password
      @user.valid?
      expect(@user.errors.full_messages).to include("Password is too long (maximum is 128 characters)")
    end
  end
end

context

特定の条件を指定してグループを分けます
使用方法はdescribeと同じですが、describeには何についてのテストなのかを指定するのに対し、cotextには特定の条件を指定

require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    @user = FactoryBot.build(:user)
  end

  describe 'ユーザー新規登録' do
    context '新規登録できるとき' do
      it 'nicknameとemail、passwordとpassword_confirmationが存在すれば登録できる' do
        expect(@user).to be_valid
      end
    end
    context '新規登録できないとき' do
      it 'nicknameが空で登録できない' do
        @user.nickname = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Nickname can't be blank")
      end
      it 'emailが空で登録できない' do
        @user.email = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Email can't be blank")
      end
      it 'passwordが空では登録できない' do
        @user.password = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Password can't be blank")
      end
      it 'passwordとpassword_confirmationが不一致では登録できない' do
        @user.password = '123456'
        @user.password_confirmation = '1234567'
        @user.valid?
        expect(@user.errors.full_messages).to include("Password confirmation doesn't match Password")
      end
      it 'nicknameが7文字以上では登録できない' do
        @user.nickname = 'aaaaaaa'
        @user.valid?
        expect(@user.errors.full_messages).to include('Nickname is too long (maximum is 6 characters)')
      end
      it '重複したemailが存在する場合は登録できない' do
        @user.save
        another_user = FactoryBot.build(:user)
        another_user.email = @user.email
        another_user.valid?
        expect(another_user.errors.full_messages).to include('Email has already been taken')
      end
      it 'emailは@を含まないと登録できない' do
        @user.email = 'testmail'
        @user.valid?
        expect(@user.errors.full_messages).to include('Email is invalid')
      end
      it 'passwordが5文字以下では登録できない' do
        @user.password = '00000'
        @user.password_confirmation = '00000'
        @user.valid?
        expect(@user.errors.full_messages).to include('Password is too short (minimum is 6 characters)')
      end
      it 'passwordが129文字以上では登録できない' do
        @user.password = Faker::Internet.password(min_length: 129, max_length: 150)
        @user.password_confirmation = @user.password
        @user.valid?
        expect(@user.errors.full_messages).to include("Password is too long (maximum is 128 characters)")
      end
    end
  end

Tweetモデル単体テストコードの記述

テストコードを記述するファイルを作成

rails g rspec:model tweet

FactoryBotを準備

spec/factories/tweets.rb

FactoryBot.define do
  factory :tweet do
    text {Faker::Lorem.sentence}
    image {Faker::Lorem.sentence}
    association :user 
  end
end

association :userは、user.rbのFactoryBotとアソシエーションがあることを意味してる
Tweetのインスタンスが生成したと同時にm関連するUserのインスタンスも生成されます
Tweetに対しては、必ずUserが紐づいている必要があるため、このように記述する必要がある
(UserはTweetを必ず持っているわけではないため、User.rbには記述しません)

exampleの整理

  • 画像とテキストを投稿できる
  • テキストのみで投稿できる
  • テキストが空では投稿できない
  • ユーザーが紐付いていなければ投稿できない
require 'rails_helper'

RSpec.describe Tweet, type: :model do
  before do
    @tweet = FactoryBot.build(:tweet)
  end

  describe 'ツイートの保存' do 
    context 'ツイートが投稿できる場合' do
      it '画像とテキストを投稿できる' do
        expect(@tweet).to be_valid
      end
      it 'テキストのみ投稿できる' do
        @tweet.image = ''
        expect(@tweet).to be_valid
      end
    end
    context 'ツイートが投稿できない場合' do
      it 'テキストが空では投稿できない' do
        @tweet.text = ''
        @tweet.valid?
        expect(@tweet.errors.full_messages).to include("Text can't be blank")
      end
      it 'ユーザーが紐づいてなければ投稿できない' do
        @tweet.user = nil
        @tweet.valid?
        expect(@tweet.errors.full_messages).to include('User must exist')
      end
    end
  end
end

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?