model_spec の準備とモデルの生成: 小林研 Rails Tips (54)

このページの内容は hkob_blog のリポジトリに1日遅れで反映されます(記事執筆前に前日分をコミットしています)。

はじめに

Rails Tips の 54 回目です。いよいよ model を作成したいと思います。

Rails をはじめよう - Railsガイド

model_spec テンプレートの準備

モデルを作成する前に model のテスト用のテンプレートを準備しておきたいです。まずファイルを用意します。

$ mkdir -p lib/templates/rspec/model
$ touch lib/templates/rspec/model/model_spec.rb
require "rails_helper"

# RSpec.describe <%= class_name %>, type: :model do
#   let(:<%= singular_table_name %>) { <%= plural_table_name %> :<%= singular_table_name %> }
#   let(:can_delete) { <%= plural_table_name %> :can_delete }
#   let(:shinagawa) { campuses :shinagawa }
#   let(:arakawa) { campuses :arakawa }
#   let(:both) { campuses :both }

#   context "属性に関する共通テスト" do
#     subject { can_delete }
#     let(:another_object) { <%= plural_table_name %> :another_object }

#     it_behaves_like "存在制約", %i[]
#     it_behaves_like "一意制約", %i[]
#     it_behaves_like "複合一意制約", %i[]
#     it_behaves_like "削除可能制約"
#     it_behaves_like "削除不可制約"
#     it_behaves_like "関連確認", :<%= singular_table_name %>, has_many: %i[], has_one: %i[], children: :optional, child: :optional
#     it_behaves_like "親削除時に自分も削除", :<%= singular_table_name %>, %i[has_many has_one]
#     it_behaves_like "親は削除不可", :<%= singular_table_name %>, %i[has_many has_one]
#     it_behaves_like "親削除時にnullを設定", :<%= singular_table_name %>, %i[has_many has_one]
#   end

#   describe "<%= class_name %> クラスについて" do
#     context "order_sort_order" do
#       subject { described_class.order_sort_order }
#       it_behaves_like "昇順確認", 0, ->(o) { o.sort_order }
#     end

#     context "order_bar_desc" do
#       subject { described_class.order_bar_desc }
#       it_behaves_like "降順確認", 99999, ->(o) { o.bar }
#     end
#
#     context "Class methods" do
#       subject { described_class }
#       it_behaves_like "gp"
#
#       it_behaves_like "単一メソッド呼び出し" do
#         let(:test_set) do
#           {
#               foo: [nil, [true, false, true]],
#           }
#         end
#       end
#     end

#     describe "get_or_create method" do
#       subject { -> { described_class.get_or_create(*params) }}
#       context '既存のものを取得' do
#         let(:params) { [object.xxx, object.yyy] }
#         it_behaves_like "オブジェクト数が変化しない?", <%= class_name %>
#       end
#
#       context '新規作成' do
#         let(:params) { [object.xxx, other.yyy] }
#         it_behaves_like "オブジェクトが1増えるか?", <%= class_name %>
#       end
#     end
#   end

#   describe "一つの <%= class_name %> オブジェクトについて" do
#     subject { <%= singular_table_name %> }

#     it_behaves_like "配列内に存在?", %w[
#       described_class.foo\ bar
#     ]
#     it_behaves_like "同じ?", %w[
#       described_class.foo\ bar
#     ]
#     it_behaves_like "配列内に存在しない?", %w[
#       described_class.foo\ bar
#     ]
#     it_behaves_like "一致しない?", %w[
#       described_class.foo\ bar
#     ]
#   end

#   context "複数の <%= class_name %> オブジェクトについて" do
#     let!(:targets) { <%= plural_table_name %>(*%i[foo bar baz]) }
#     subject { targets }

#     it_behaves_like "配列メソッド呼び出し" do
#       let(:test_set) do
#         {
#             foo: [nil, [true, false, true]],
#         }
#       end
#     end
#   end
# end

6.1 モデルを作成する

テンプレートが準備できたので、ガイドの通りにジェネレータを実行します。個人的にモデル名を大文字(クラス名)で書いたことがないので、いつもの通りに小文字にします。

$ bin/rails g model article title:string body:text
      invoke  active_record
      create    db/migrate/20240122091842_create_articles.rb
      create    app/models/article.rb
      invoke    rspec
      create      spec/models/article_spec.rb

ここでガイドでは何も修正せずに migrate をしているのですが、後で title や body に空でないという制約をつけることになるので、以下のように null: false を付ける修正をしておきます。

class CreateArticles < ActiveRecord::Migration[7.1]
  def change
    create_table :articles do |t|
      t.string :title, null: false
      t.text :body, null: false

      t.timestamps
    end
  end
end

6.2 データベースマイグレーション

migration ファイルを修正したので、ガイド通りマイグレーションを行います。今回は development だけでなく、RAILS_ENV=test についても実施しておきます。

$ bin/rails db:migrate
== 20240122091842 CreateArticles: migrating ===================================
-- create_table(:articles)
   -> 0.0054s
== 20240122091842 CreateArticles: migrated (0.0054s) ==========================

$ bin/rails db:migrate RAILS_ENV=test
== 20240122091842 CreateArticles: migrating ===================================
-- create_table(:articles)
   -> 0.0051s
== 20240122091842 CreateArticles: migrated (0.0051s) ==========================

おわりに

このあとガイドでは、console でモデルを直接編集するターンに入るのですが、その前にテストを実施したいと思うので、今日はここまでにしておきます。明日は 2 日目から 5 日目までに説明した shared_example を利用してテストを実施します。