🙆‍♀️

Rails7.13 サクッとRSpecを導入して単体テストする

2024/02/17に公開

はじめに

サクッとテストフレームワークRSpecを導入してモデルの単体テストの書き方について記述します。

実行環境

  • M2 mac mini
  • Rails 7.1.3
  • Ruby 3.2.3
  • Mysql 8.0
  • Docker 25.0.2
  • vscodeエディタ

環境構築が済んでいない方はこちらを参考に環境構築を行なってください。
https://zenn.dev/kei1232/articles/0fac51829570c1

Schemafileがまだの方はこちらから設定してください。
https://zenn.dev/kei1232/articles/5c31cc58453453

RSpecの導入

テストに必要なGemをGemfileに記述します。

Gemfile
group :development, :test do
  # テストフレームワーク
  gem 'rspec-rails'
  # テスト用オブジェクトの生成gem
  gem 'factory_bot_rails'
  # ダミーデータの作成gem
  gem "faker"
  ~~~~~~~~ 省略 ~~~~~~~~
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
end

追加したGemをインストール

docker-compose exec web bundle install

RSpecのセットアップ

docker-compose exec web rails g rspec:install

作成されるファイル

create  spec # specの設定ファイル
create  spec/spec_helper.rb # Railsの設定ファイル
create  spec/rails_helper.rb # RSpecの全体的な設定ファイル

.rspecに以下の設定を追記すると、出力がドキュメント形式や色付けされて見やすくなります。

--require spec_helper
# 以下2つを追加
--color
--format documentation

FactoryBot設定

config.include FactoryBot::Syntax::Methodsを追加します。

spec/rails_helper.rb
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
省略
 ~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~

begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  abort e.to_s.strip
end
RSpec.configure do |config|
fixtures
  config.fixture_paths = [
    Rails.root.join('spec/fixtures')
  ]
  ~~~~~~~~~~~~~~ 追加 ~~~~~~~~~~~~~~~~
  # FactoryBot設定
  config.include FactoryBot::Syntax::Methods
  ~~~~~~~~~~~~~~ 追加 ~~~~~~~~~~~~~~~~

  ~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~
end

自動生成ファイルの設定

config/application.rb
module App
  class Application < Rails::Application
    config.load_defaults 7.1

    config.autoload_lib(ignore: %w(assets tasks))
 
    ~~~~~~~~ 省略 ~~~~~~~~
    # ここから
    config.generators do |g|
      g.test_framework :rspec, # テストフレームワークとしてRSpecを指定
      request_specs: false, # リクエストスペックを作成しない
      fixtures: false, # テストデータを作るfixtureを作成しない
      view_specs: false, # ビュー用のスペックを作成しない
      helper_specs: false, # ヘルパー用のスペックを作成しない
      routing_specs: false # ルーティングのスペックを作成しない
    end
    # ここまでを追加
  end
end

controller、modelを生成すると、テスト用のファイルも生成できます。
生成されるテストファイルをRSpecに変更、Specファイルが生成されないように、上記の設定を行います。

以上でRSpecの設定は完了です。

SchemafileにTaskテーブルを記述

options = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"

create_table "tasks", force: :cascade, options: options do |t|
  t.string :title, null: false
  t.string :content, null: false
  t.boolean :is_completed, default: false, null: false
  t.datetime :created_at, null: false
  t.datetime :updated_at, null: false
end

Taskテーブルをマイグレーション

docker-compose exec web rake ridgepole:apply RAILS_ENV=test

RSpe Model作成

テスト用のtaskモデルの作成。「--skip-migration」コマンドでマーグレーションをスキップします。

docker-compose exec web rails g model Task --skip-migration

作成ファイル

      invoke  active_record
      create    app/models/task.rb
      invoke    rspec
      create      spec/models/task_spec.rb
      invoke      factory_bot
      create        spec/factories/task.rb

FactoryBot と Faker を使ってテストデータを生成する。
どんなものが用意されているかは公式リファレンスから確認してください。
https://github.com/faker-ruby/faker/blob/main/README.md

モデルのバリデーション

app/models/task.rb
class Task < ApplicationRecord
  validates :title, presence: true, length: { maximum: 10 } # titleが空でない&10文字以内であること
  validates :content, presence: true, length: { maximum: 100 } # contentが空でない&100文字以内であること
end

テストデータの作成

spec/factories/tasks.rb
FactoryBot.define do
  factory :task do
    # 英数字のランダムな文字列を生成する(10文字)
    title { Faker::Lorem.characters(number: 10) }
    # 英数字のランダムな文字列を生成する(100文字)
    content { Faker::Lorem.characters(number: 100) }
    is_completed { false }
    created_at { DateTime.now }
    updated_at { DateTime.now }
  end
end

モデルのテスト

spec/models/task_spec.rb
require 'rails_helper'

RSpec.describe Task, type: :model do
  # given データの準備
  let(:task) { build(:task) } # モデルのテストデータを準備

  describe "title、contentのバリデーション" do # テストの対象が何かを記述する。
    it "title、contentのバリデーションが通ること" do # 期待値を記述する
      # when 変化
      # then 結果を比較
      expect(task).to be_valid
    end
  end

  describe "titleのバリデーション" do # テストの対象が何かを記述する。

    it "titleが空の場合はバリデーションする" do # 期待値を記述する
      # when 変化
      task.title = ''
      # then 結果を比較
      expect(task).to_not be_valid
    end

    it "titleが10文字を超える場合はバリデーションする" do
      # when 変化
      task.title = 'a' * 11
      # then 結果を比較
      expect(task).to_not be_valid
    end
  end

  describe "contentのバリデーション" do # テストの対象が何かを記述する。

    it "contentが空の場合はバリデーションする" do # 期待値を記述する
      # when 変化
      task.content = ''
      # then 結果を比較
      expect(task).to_not be_valid
    end

    it "contentが100文字を超える場合はバリデーションする" do
      # when 変化
      task.content = 'a' * 101
      # then 結果を比較
      expect(task).to_not be_valid
    end
  end
end

テストの実行

docker-compose exec web rspec spec/models/task_spec.rb

テスト結果

Task
  title、contentのバリデーション
    title、contentのバリデーションが通ること
  titleのバリデーション
    titleが空の場合はバリデーションする
    titleが10文字を超える場合はバリデーションする
  contentのバリデーション
    contentが空の場合はバリデーションする
    contentが100文字を超える場合はバリデーションする

Finished in 0.03728 seconds (files took 0.9696 seconds to load)
5 examples, 0 failures

まとめ

以上の設定によりテストを書く準備ができました。テストを書いてrailsライフを楽しみましょう。

Discussion