新しいRails 8アプリ作成したら、テストはRSpecでやる!
テストを書く
パート1はこちら 新しいRails 8アプリを作ろう!オススメGem ー 合同会社Sampo Development
作成したばかりだと、テストフレームワークはMinitestになっていて、 それをRSpecに置き換えることができます。ほとんどのプロジェクトだと RSpecを使っているかと思いますが、Rails自体はMinitestでテストされていて、 Basecampなど有名なRailsを使っているところもMinitestを使っています。
わかりやすさでいうと、やはりRSpecはわかりやすいと思うので、今回もRSpecを使って テスト書きたいと思います。
echo 'gem "rspec-rails", group: [:development, :test]' >> Gemfile
というコマンドでRSpecをGemfileに追加します。(bundle実行とrails generate rspec:installは必要ですが)
# config/application.rb
config.generators do |g|
g.test_framework :rspec, fixture: false
end
application.rbでテストフレームワークをRSpecに指定します。
rails generate scaffold TimeEntry clocked_in:datetime clocked_out:datetime
今回は時間管理したいので、とりあえずTimeEntryを作成します。 これで打刻できるようにしたいです。
まだユーザーモデルなどもないので、簡単に行きましょう。 テストの設定があっているかどうかという確認にもなります。
invoke active_record
create db/migrate/20220108013901_create_time_entries.rb
create app/models/time_entry.rb
invoke rspec
create spec/models/time_entry_spec.rb
invoke resource_route
route resources :time_entries
invoke scaffold_controller
create app/controllers/time_entries_controller.rb
invoke tailwindcss
create app/views/time_entries
create app/views/time_entries/index.html.erb
create app/views/time_entries/edit.html.erb
create app/views/time_entries/show.html.erb
create app/views/time_entries/new.html.erb
create app/views/time_entries/_form.html.erb
create app/views/time_entries/_time_entry.html.erb
invoke resource_route
invoke rspec
create spec/requests/time_entries_spec.rb
invoke helper
create app/helpers/time_entries_helper.rb
invoke rspec
create spec/helpers/time_entries_helper_spec.rb
といろんなファイルが作成されました。中身を確認すると、ちゃんとTailwindが使用されていて、 テストもspecフォルダにはいって、ちゃんとRSpecで作成されています。
では、テストは少し作業が残っています。 - あっている値 - まちがっている値
をいれて、走らせられるようにしましょう!
# spec/models/time_entry_spec.rb
let(:valid_attributes) {
{
clocked_in: Time.now,
}
}
let(:invalid_attributes) {
{
clocked_in: Time.now - 1.year,
}
}
のように変更して、上は正しい値をいれて、下は間違っている値を入れます。 テストはRailsが作成してくれたままにしましょう。
bin/rails db:migrate RAILS_ENV=test
でDBセットアップして、rspecコマンド実行します。
/time_entries POST /create with invalid parameters does not create a new TimeEntry
というエラーが出ましたね。 一年前の時間も打刻できてしまっています。 バリデーション追加しないといけないですね。
Rails 7で追加されたComparisonValidatorを使えば、 Gemを足さなくても日付の比較バリデーションが書けます。
# app/models/time_entry.rb
class TimeEntry < ApplicationRecord
validates :clocked_in, comparison: { greater_than_or_equal_to: -> { Time.zone.now - 5.minutes } }
end
エラーの場合はTurboの都合で422、unprocessable entityを返すように変わっているので、 それでテストが落ちる場合は以下のように書き換えます。
# spec/requests/time_entries_spec.rb # patch
expect(response).to have_http_status(:unprocessable_content)
これでテストが全部通るようになりました! * 地味にclocked_inの値の追加は何箇所かでやらないといけなかったですが。
次回は翻訳の追加ですね。
2026年追記:MinitestとFixturesもオススメ
この記事を書いた時はRSpecを推していましたが、最近はMinitestとFixturesの組み合わせも 非常にオススメです。
RSpecは表現力が高いですが、DSLが多くて覚えることが多いです。 MinitestはRuby標準ライブラリに含まれていて、追加のGemが不要で、 普通のRubyのコードでテストが書けるのがシンプルでいいところです。
また、テストデータについてはFactoryBotよりもRails標準のFixturesが 実はかなり使いやすいです。Fixturesのメリットは:
- 速い — Fixturesはテスト開始時にまとめてDBに入るので、テストごとにレコードを作成するFactoryBotより高速
- Rails標準 — 追加のGemが不要で、Railsのアップデートにも影響されにくい
- シンプル — YAMLファイルでテストデータを定義するだけ
# test/fixtures/time_entries.yml
morning_entry:
clocked_in: <%= Time.current %>
clocked_out: <%= Time.current + 8.hours %>
late_entry:
clocked_in: <%= Time.current - 1.year %>
# test/models/time_entry_test.rb
class TimeEntryTest < ActiveSupport::TestCase
test "valid time entry" do
entry = time_entries(:morning_entry)
assert entry.valid?
end
test "rejects old clocked_in time" do
entry = time_entries(:late_entry)
assert_not entry.valid?
end
end
RSpecに慣れている方も、一度Minitest + Fixturesを試してみてください。 特に新しいプロジェクトではセットアップがほぼゼロで始められるので、手軽です。