【Rails】RSpecによるテスト 2.モデルのテスト


RSpecを使ったモデルのテスト

前回の記事にてRSpecの準備を行いました。

今回はRSpecを使ってモデルの単体テストを行っていきます。

一例として、ブログのuserモデルについてテストを行います。
このブログでは、新規ユーザーを登録する際に

  • ユーザー名
  • emailアドレス(重複なし)
  • パスワード(6文字以上)
  • パスワード確認(上のパスワードと同じ内容)

の入力が必要となります。
バリデーションの設定により、例えばユーザー名が空欄だと新規登録できません。
実際にそのように設定されているかテストを行います。

まずは基本的な部分の記述です。

spec/models/user_spec.rb
require 'rails_helper'

describe User do
  describe 'テスト項目' do
    it "テストの内容" do
    end
  end
end

1行目のrequireは忘れずに記述しましょう。
rails_helper内のメソッドや設定を読み込まなくなってしまいます。

あとはdescribe内に確認したいテストの項目(どんなテストかわかれば大丈夫です)を、
it内に実際に動かすテストの内容を書いていきます。
今回のテスト名はコントローラーのアクションを意識してUser#createとしておきます。

user_spec.rb
require 'rails_helper'

describe User do
  describe '#create' do
    it "is invalid without name" do
      user = User.new(name: "", email: "[email protected]", password: "12345678", password_confirmation: "12345678")
      user.valid?
      expect(user.errors[:name]).to include("can't be blank")
    end
  end
end
  • 新しいユーザーを作成
    今回のテストでは、名前が空欄だと登録できないことを確認したいのでnameは空欄、emailとpasswordは適当に入力します。passwordの確認があるので、password_confirmationにもpasswordと同じ文字列を入力します。

  • valid?メソッド
    このメソッドは、バリデーションにより保存ができないかどうかを確認します。
    問題なく保存できる場合はtrueを返すのみですが、バリデーションにより保存できない場合にはfalseを返すと共に、なぜ保存できないかのエラーメッセージが確認できるようになります。エラーメッセージはerrorsメソッドで呼び出します。

  • expect(〜).to include(〜)
    expectで示す内容が、includeで示す値を含むかをチェックします。
    ですが、文字列を指定する場合に使う""(ダブルクォーテーション)まで一致するかを確認されますので、実際には同じ文章となっているかがチェックされます。

この状態でターミナルからRSpecを起動させ、テストを実行してみます。

User
  #create
    is invalid without name

Finished in 0.29412 seconds (files took 1.63 seconds to load)
1 example, 0 failures

FAILEDなどの文字が出てきていなければテストは成功です。

factory_botの活用

これでは新規ユーザーの作成がかなり長い記述になりますので、factory_botを使って簡略化させます。

spec/factories/user.rb
FactoryBot.define do
  factory :user do
    name { "name" }
    email { "[email protected]" }
    password { "12345678" }
    password_confirmation { "12345678" }
  end
end
user_spec.rb
require 'rails_helper'

describe User do
  describe '#create' do
    it "is invalid without name" do
      user = build(:user, name: nil)
      user.valid?
      expect(user.errors[:name]).to include("can't be blank")
    end
  end
end

factory_botを使うことで、ユーザーを新規作成する時のそれぞれの項目を指定しておくことができます。
複数のテストを行う中で何度もユーザーを新規作成する長ったらしい記述をする必要が無くなります。

Fakerの活用

ちなみにFakerを使ってfactory_botの記述をすると、一例として以下のようになります。

factories/user.rb
FactoryBot.define do
  password = Faker::Internet.password(min_length: 6, max_length: 8)

  factory :user do
    name { Faker::Internet.username(specifier: 5..6)}
    email { Faker::Internet.email }
    password { password }
    password_confirmation { password }
  end
end

Fakerを使うと、名前やメールアドレスなどのダミーデータを作成してくれます。
自動でランダムに値を生成してくれるので、テストの中で複数のユーザーを作成する場合でも名前やアドレスが重複しなくなります。

passwordは確認のため2回入力を求められますが、passwordと確認は同じ文字列でないといけないので、一度変数に代入して同じ値を使うようにしています。
password変数を2行目で指定していますが、factory :user do以下に記述しても問題ないようです。

場合分けをして条件を網羅したテストを行う

あとは確認したい状況に応じたテストを作成していきます。
今回の例でいうと、確認したい条件は
- 名前、emailアドレス、パスワードがあれば登録できる
- 名前が空欄だと登録できない
- emailアドレスが空欄だと登録できない
- パスワードが空欄だと登録できない
- パスワードが6文字以上であれば登録できる
- パスワードが5文字以下だと登録できない
- パスワードと確認が一致していないと登録できない
- 登録済みのemailアドレスでは登録できない

このくらいでテストしてみます。

user_spec.rb
require 'rails_helper'

describe User do
  describe '#create' do
    it "名前、emailアドレス、パスワードがあれば登録できる" do
      user = build(:user)
      expect(user).to be_valid
    end

    it "名前が空欄だと登録できない" do
      user = build(:user, name: nil)
      user.valid?
      expect(user.errors[:name]).to include("can't be blank")
    end

    it "emailアドレスが空欄だと登録できない" do
      user = build(:user, email: nil)
      user.valid?
      expect(user.errors[:email]).to include("can't be blank")
    end

    it "パスワードが空欄だと登録できない" do
      user = build(:user, password: nil)
      user.valid?
      expect(user.errors[:password]).to include("can't be blank")
    end

    it "パスワードが6文字以上であれば登録できる" do
      password = Faker::Internet.password(min_length: 6, max_length: 6)
      user = build(:user, password: password, password_confirmation: password)
      expect(user).to be_valid
    end

    it "パスワードが5文字以下だと登録できない" do
      password = Faker::Internet.password(min_length: 5, max_length: 5)
      user = build(:user, password: password, password_confirmation: password)
      user.valid?
      expect(user.errors[:password]).to include("is too short (minimum is 6 characters)")
    end

    it "パスワードと確認が一致していないと登録できない" do
      user = build(:user, password_confirmation: "")
      user.valid?
      expect(user.errors[:password_confirmation]).to include("doesn't match Password")
    end

    it "登録済みのemailアドレスでは登録できない" do
      email = Faker::Internet.email
      user = create(:user, email: email)
      user2 = build(:user, email: email)
      user2.valid?
      expect(user2.errors[:email]).to include("has already been taken")
    end

  end
end

これでテストを実行してみます。

ターミナル
$ bundle exec rspec

下記のように表示されれば今回のテストは成功です。

User
  #create
    名前、emailアドレス、パスワードがあれば登録できる
    名前が空欄だと登録できない
    emailアドレスが空欄だと登録できない
    パスワードが空欄だと登録できない
    パスワードが6文字以上であれば登録できる
    パスワードが5文字以下だと登録できない
    パスワードと確認が一致していないと登録できない
    登録済みのemailアドレスでは登録できない

Finished in 0.26533 seconds (files took 2.81 seconds to load)
8 examples, 0 failures