バリデーションをテストしよう
- 学習の目標
- はじめに
- バリデーションとは
- 基本的なバリデーションの追加
- 基本的なバリデーションのテスト
- テストの実行と確認
- 追加のバリデーション設定
- テストコードの構造化
- テストコードの改善:subjectとletの活用
- まとめ
学習の目標
本章では、以下の内容を学習します。
- Railsにおけるバリデーションの役割と重要性を理解する
- 基本的なバリデーション(presence、length)の実装方法を学ぶ
- バリデーションに対するテストの書き方を習得する
- contextを使ったテストの構造化について理解する
- subjectとletを活用したテストコードのリファクタリング手法を学ぶ
はじめに
前章では、モデルテストの基本的な書き方を学びました。今回は、より実践的な内容としてバリデーションのテストについて学んでいきます。
バリデーションは、データベースに保存されるデータの品質を保証するための仕組みです。たとえば、ユーザー登録の際にメールアドレスが正しい形式かどうかをチェックしたり、投稿記事のタイトルが空でないことを確認したりします。
適切なバリデーションを設定することで、不正なデータがデータベースに保存されることを防ぎ、アプリケーションの安定性を保つことができます。そして、これらのバリデーションが正しく動作することを確認するために、テストを書くことが大切です。
バリデーションとは
**バリデーション(validation)**は、モデルのデータが適切な状態であることを確認するための仕組みです。
Railsでは、モデルクラスにvalidatesメソッドを使って、様々な検証ルールを簡単に追加できます。データの保存前に自動的にチェックが行われ、ルールに違反する場合は保存が拒否されます。
実際の開発では、ほぼすべてのモデルで何らかのバリデーションを使用します。これにより、データの整合性を保ち、予期しないエラーを防ぐことができます。
基本的なバリデーションの追加
それでは、前章で作成したArticleモデルにバリデーションを追加してみましょう。
app/models/article.rbファイルを開いて、以下のように編集してください。
class Article < ApplicationRecord  validates :title, presence: trueendこの一行を追加することで、title属性が必須項目になりました。presence: trueは、その属性が空でないことを確認するバリデーションです。
これにより、タイトルが空の記事は保存できなくなります。実際のブログアプリケーションでも、タイトルのない記事は意味をなさないので、このようなバリデーションは必須といえるでしょう。
基本的なバリデーションのテスト
バリデーションを追加したら、それが正しく動作することをテストで確認しましょう。
spec/models/article_spec.rbを以下のように更新してください。
require 'rails_helper'
RSpec.describe Article, type: :model do  it 'タイトルがあれば有効' do    article = Article.new(title: 'RSpecの基本')    expect(article).to be_valid  end
  it 'タイトルがなければ無効' do    article = Article.new(title: nil)    expect(article).not_to be_valid  endend最初のテストは、タイトルが設定されている場合に記事が有効であることを確認しています。
2つ目のテストでは、タイトルがnilの場合に記事が無効になることを確認しています。not_toを使うことで、「〜でないことを期待する」という否定的な検証ができます。
テストの実行と確認
テストを書いたら、実際に動作を確認してみましょう。
bundle exec rspec以下のような結果が表示されれば成功です。
Article  タイトルがあれば有効  タイトルがなければ無効
Finished in 0.12345 seconds (files took 1.23 seconds to load)2 examples, 0 failures両方のテストが通りました。これで、タイトルの必須チェックが正しく動作していることが確認できました。
追加のバリデーション設定
次に、contentフィールドにもバリデーションを追加してみましょう。記事の本文は、ただ存在するだけでなく、ある程度の長さも必要です。
app/models/article.rbを以下のように更新してください。
class Article < ApplicationRecord  validates :title, presence: true  validates :content, presence: true, length: { minimum: 10 }endこの設定により、contentに対して2つのバリデーションが追加されました。
まずpresence: trueで、contentが空でないことを確認します。さらにlength: { minimum: 10 }で、contentが最低10文字以上であることを確認します。
短すぎる記事は読者にとって価値が低いため、このような最小文字数の制限を設けることは実践的な設定といえます。
テストコードの構造化
新しいバリデーションに対応するため、テストコードを拡張しましょう。このとき、contextを使ってテストを論理的にグループ化すると、より読みやすくなります。
require 'rails_helper'
RSpec.describe Article, type: :model do  context '正常系' do    it 'タイトルとコンテンツがあれば有効' do      article = Article.new(        title: 'RSpecの基本',        content: 'RSpecを学びましょう。楽しいですよ!'      )      expect(article).to be_valid    end  end
  context '異常系' do    it 'タイトルがなければ無効' do      article = Article.new(title: nil)      expect(article).not_to be_valid    end
    it 'コンテンツがなければ無効' do      article = Article.new(        title: 'RSpecの基本',        content: nil      )      expect(article).not_to be_valid    end
    it 'コンテンツが10文字未満なら無効' do      article = Article.new(        title: 'RSpecの基本',        content: '短すぎる'      )      expect(article).not_to be_valid    end  endendcontextを使うことで、「正常系」と「異常系」のテストを明確に分けることができました。
正常系では、すべてのバリデーションを満たす場合のテストを行い、異常系では、各バリデーションに違反する場合のテストを個別に行っています。
このような構造化により、どのようなケースをテストしているのかが一目でわかるようになります。
テストコードの改善:subjectとletの活用
テストコードをさらに改善してみましょう。同じようなコードが繰り返されている部分を、subjectとletを使ってリファクタリングします。
require 'rails_helper'
RSpec.describe Article, type: :model do  subject { article.valid? }
  let(:title) { 'RSpecの基本' }  let(:content) { 'RSpecを学びましょう。楽しいですよ!' }  let(:article) { Article.new(title: title, content: content) }
  context '正常系' do    it 'タイトルとコンテンツがあれば有効' do      expect(subject).to be true    end  end
  context '異常系' do    context 'タイトルがない場合' do      let(:title) { nil }
      it '無効である' do        expect(subject).to be false      end    end
    context 'コンテンツがない場合' do      let(:content) { nil }
      it '無効である' do        expect(subject).to be false      end    end
    context 'コンテンツが10文字未満の場合' do      let(:content) { '短すぎる' }
      it '無効である' do        expect(subject).to be false      end    end  endendこのリファクタリングで行った改善点を見ていきましょう。
subjectの活用
subject { article.valid? }により、各テストで検証したい内容(記事が有効かどうか)を明確に定義しています。これにより、各テストケースではexpect(subject).to be trueのように簡潔に書けるようになりました。
letによる変数管理
letを使うことで、テストで使用するデータを効率的に管理できます。デフォルトの値を最初に定義し、各テストケースで必要に応じて上書きすることで、コードの重複を減らしています。
contextの入れ子構造
異常系のテストをさらに細かく分類することで、どのような条件でテストしているのかがより明確になりました。
まとめ
本章では、モデルバリデーションとそのテストについて学びました。以下の内容を理解できたことと思います。
- バリデーションがデータの整合性を保つ仕組みであること
- validatesメソッドを使った基本的なバリデーションの設定方法
- presenceと- lengthバリデーションの使い方
- 正常系と異常系を分けたテストの書き方
- contextを使ったテストの論理的な構造化
- subjectと- letを活用したテストコードのリファクタリング
バリデーションテストは、アプリケーションの品質を保つために欠かせない要素です。今回学んだ基本的なパターンを応用すれば、より複雑なバリデーションのテストも書けるようになるでしょう。
次の章では、モデル間の関連(アソシエーション)についてテストする方法を学んでいきます。
Basicプランでより詳しく学習
この先のコンテンツを読むにはBasicプラン以上が必要です。より詳細な解説、実践的なサンプルコード、演習問題にアクセスして学習を深めましょう。