【Rails】RSpecとは - ユーザーモデルの単体テストをやってみる -
RSpecとは
Ruby向けのテストフレームワークで、プログラムが正しく動作しているかを確認するツールです。
単体テストと結合テスト
主要なテストに以下の2つがあります。
単体テスト(Unit Testing)
クラスやメソッドが正しく動作するかテストします。
例:
- タイトルの文字数制限を超えたときにエラーがでるか
- 正しいユーザー名とパスワードでログインできるか
統合テスト(Integration Testing)
複数の単体テストの組み合わせをテストします。
例:
- カテゴリー一覧から特定のカテゴリーページに遷移できるか
- ユーザーがログインし、記事を投稿できるか
テストをする理由
-
バグの早期発見
コードを自動的にテストし、バグの早期発見をすることで、本番環境に影響を与える前に修正できます。 -
コードの可読性向上
テストコードはコード全体の理解に役立つドキュメンテーションとしての役割にもなります。テストはコードがどのように動作するかを具体的に示すものなので、コードの利用方法や動きが文書化されたものとして利用できます。 -
品質管理
テストを通じて客観的なデータとして品質を担保できるため、クライアントやユーザーからの信頼を築くことができます。
RSpecの実装
RSpecの実装方法について説明します。
gemのインストール
以下を追記し、bundle installします。
:
group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 3.26'
gem 'selenium-webdriver', '>= 4.0.0.rc1'
# Easy installation and use of web drivers to run system tests with browsers
gem 'webdrivers'
+ gem 'rspec-rails'
+ gem 'factory_bot_rails'
+ gem 'faker'
end
:
-
gem 'rspec-rails'
RSpecを導入します。 -
gem 'factory_bot_rails'
Factory Botはテストデータの生成を助けるためのライブラリで、テスト時にデータを簡単に作成できます。 -
gem 'faker'
Fakerはダミーデータを生成するためのツールで、テストデータやサンプルデータを作成するのに便利です。
RSpecを使用のための設定
- ターミナルからRSpecのインストール
rails g rspec:install
-
spec
フォルダの配下にfactories
フォルダ作成 - FactoryBotの設定
FactoryBotが使えるようspec/rails_helper.rb
の最後のend
前に以下の記述をします。spec/fails_helper.rbconfig.include FactoryBot::Syntax::Methods end
FactoryBotにテストデータの定義を作成
アプリケーションに合わせて必要なテストデータを作成します。
例えばuserモデルのテストデータを作ってみます。
FactoryBot.define do
factory :user do
nickname { Faker::Lorem.characters(number: 10) }
account { Faker::Lorem.characters(number: 10) }
email { Faker::Internet.email }
password { "password" }
password_confirmation { "password" }
# 正しいYouTubeチャンネルのURL形式
channel { "https://www.youtube.com/channel/CHANNEL_ID" }
factory :user_with_invalid_channel do
# 正しくないYouTubeチャンネルのURL形式
channel { "https://example.com" }
end
end
end
モデルの単体テスト
userモデルのテストをやってみます。
-
spec
フォルダの配下にmodels
というフォルダを作成 -
作成した
models
フォルダの配下にuser_spec.rb
というファイルを作成 -
作成したファイルに以下を記述します。
spec/models/user_spec.rb# frozen_string_literal: true require 'rails_helper'
-
# frozen_string_literal: true
文字列の変更、バグを防ぐため。 -
require 'rails_helper'
RSpecの設定を読み込むため。
-
-
テストコード記述
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe User, type: :model do
subject { user.valid? }
let(:user) { build(:user) } # FactoryBotを使用してUserインスタンスを作成
describe 'validations' do
context 'nicknameカラム' do
it '空欄でないこと' do
user.nickname = ''
is_expected.to eq false
end
it '2文字以上であること: 1文字は×' do
user.nickname = Faker::Lorem.characters(number: 1)
is_expected.to eq false
end
it '2文字以上であること: 2文字は〇' do
user.nickname = Faker::Lorem.characters(number: 2)
is_expected.to eq true
end
it '20文字以下であること: 21文字は×' do
user.nickname = Faker::Lorem.characters(number: 21)
is_expected.to eq false
end
it '20文字以下であること: 20文字は〇' do
user.nickname = Faker::Lorem.characters(number: 20)
is_expected.to eq true
end
end
context 'accountカラム' do
it '空欄でないこと' do
user.account = ''
is_expected.to eq false
end
it '3文字以上であること: 2文字は×' do
user.account = Faker::Lorem.characters(number: 2)
is_expected.to eq false
end
it '3文字以上であること: 3文字は〇' do
user.account = Faker::Lorem.characters(number: 3)
is_expected.to eq true
end
it '20文字以下であること: 21文字は×' do
user.account = Faker::Lorem.characters(number: 21)
is_expected.to eq false
end
it '20文字以下であること: 20文字は〇' do
user.account = Faker::Lorem.characters(number: 20)
is_expected.to eq true
end
it '正しいフォーマットであること: 数字とアルファベット、ハイフン、アンダースコアが許可される' do
valid_accounts = ['user123', 'user-name', 'user_name']
invalid_accounts = ['user@123', 'user.name', 'user$name']
valid_accounts.each do |account|
user.account = account
expect(user).to be_valid
end
invalid_accounts.each do |account|
user.account = account
expect(user).not_to be_valid
end
end
end
context 'emailカラム' do
it '空欄でないこと' do
user.email = ''
is_expected.to eq false
end
it '有効なメールアドレスの形式であること' do
valid_emails = ['user@example.com', 'test.user@gmail.com', 'test_user123@example.co.jp']
invalid_emails = ['invalid_email', 'user@.com', 'user@example']
valid_emails.each do |email|
user.email = email
expect(user).to be_valid
end
invalid_emails.each do |email|
user.email = email
expect(user).not_to be_valid
end
end
end
context 'channelカラム' do
it '正しいYouTubeチャンネルのURL形式であること' do
user.channel = 'https://www.youtube.com/channel/CHANNEL_ID'
is_expected.to eq true
end
it '正しくないYouTubeチャンネルのURL形式であること' do
user.channel = 'https://example.com'
is_expected.to eq false
end
end
context 'active_for_authentication?' do
it 'activeステータスのユーザーはログインできること' do
user.status = 'active'
expect(user.active_for_authentication?).to eq true
end
it 'bannedステータスのユーザーはログインできないこと' do
user.status = 'banned'
expect(user.active_for_authentication?).to eq false
end
it 'inactiveステータスのユーザーはログインできないこと' do
user.status = 'inactive'
expect(user.active_for_authentication?).to eq false
end
end
end
end
今回テストをかけたモデルの抜粋は次の通りです。
class User < ApplicationRecord
:
validates :account, presence: true, format: { with: /\A[a-z0-9_-]+\z/ }, length: { minimum: 3, maximum: 20 }
validates :nickname, presence: true, length: {minimum: 2, maximum: 20 }
validates :email, presence: true, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i, message: "は有効なメールアドレスの形式である必要があります" }
validates :channel, format: { with: /\A\z|https?:\/\/(?:www\.)?youtube\.com\/channel\/[\w-]+\z/, message: "は正しいYouTubeチャンネルのURL形式ではありません" }
# 会員ステータス
enum status: { active: 0, banned: 1, inactive: 2 }
# active会員のみログインできる
def active_for_authentication?
super && (status == 'active')
end
:
end
テストの実行
ターミナルでテストしたいファイル名を実行します。
今回は以下の通り。
rspec spec/models/user_spec.rb
エラーがないと次のようになります!
テストコードを書くのって色んなケースや条件を考えないといけないのでとても勉強になりました。
これは慣れだな~
userのテストは汎用性があるかと思い今回記事にしてみました。
参考になったら嬉しいです。
最後まで読んでいただきありがとうございました。
Discussion