2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RSpec:モデルの単体テストの記述例

Last updated at Posted at 2021-02-28

※2022年から技術系の記事は個人ブログに投稿しております。ぜひこちらもご覧ください→yamaday0u Blog

今回は前回の記事「RSpecの導入&関係の深いgemについて」の続きで、モデルの単体テストを行います。

前回の記事の各gemのインストール、初期設定が完了していることを前提としています。

モデルのテストファイルを作成

ターミナルでrails g rspec model モデル名コマンドを入力してテストファイルを生成します。

ターミナル
$ rails g rspec model モデル名
#以下のように表示されればテストファイルの生成が成功しています。
Running via Spring preloader in process 1234
      create  spec/models/モデル名_spec.rb
      invoke  factory_bot
      create    spec/factories/モデル名の複数形.rb

たとえばuserモデルのテストファイルを生成するとこうなります。

ターミナル
$ rails g rspec:model user
Running via Spring preloader in process 1234
      create  spec/models/user_spec.rb
      invoke  factory_bot
      create    spec/factories/users.rb #Factory Botをインストールしておくと自動生成される

ちなみに事前にFactory Botもインストールしておくと、spec/factories/users.rbは自動生成されます。

生成したuser_spec.rbにはこのような記述がされています。

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

do〜endの間にテストコードを記述していきます。

また1行目のrequire 'rails_helperは、前回の記事で行ったrails g rspec:installコマンドで生成したrails_helper.rbを読み込んでいます。

FactoryBotの設定

まずは、FactoryBotでUserモデルのテストで使用するインスタンスを設定してまとめます。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name { Faker::Name }
    email { Faker::Internet.free_email }
    password = Faker::Internet.password(min_length: 6)
    password { password }
    password_confirmation { password }
  end
end

passwordカラムとpassword_confirmationカラムは同じ値を入れるため、変数passwordに値を代入して、各カラムに変数passwordを入れます。

Faker::の後ろに続くNameInternet.free_emailでどんな値をランダムに生成するかを指定しています。前者なら名前、後者ならメールアドレスですね。

Fakerでは色んなタイプの値を生成してくれるので、Fakerの公式GitHubで見てみてください。

テストコードの設定

Userモデル(usersテーブル)にnameカラム、emailカラム、passwordカラムがあると想定して、
たとえば以下のようにユーザー新規登録機能のテストコードを記述します。

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    # 各テストコードが実行される前にFactoryBotで生成したインスタンスを@userに代入
    @user = FactoryBot.build(:user)
  end

  # describeでテスト対象をグループ分け
  describe '新規登録' do
    # contextでどのような状況なのかをグループ分け
    context '新規登録できるとき' do
      # itで何をテストしているのかをグループ分け
      it '全ての項目が入力されていれば登録できる' do
        expect(@user).to be_valid
      end
    end

    context '新規登録できないとき' do
      it 'nameが空では登録できない' do
        @user.name = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Name can't be blank")
      end
      it 'emailが空では登録できない' do
        @user.email = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Email can't be blank")
      end
      it '重複したemailが存在する場合登録できない' do
        @user.save
        another_user = FactoryBot.build(:user)
        another_user.email = @user.email
        another_user.valid?
        expect(another_user.errors.full_messages).to include('Email has already been taken')
      end
      it 'emailに@が含まれていないと登録できない' do
        @user.email.slice!('@')
        @user.valid?
        expect(@user.errors.full_messages).to include('Email is invalid')
      end
      it 'passwordが空では登録できない' do
        @user.password = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Password can't be blank")
      end
      it 'passwordが5文字以下では登録できない' do
        @user.password = '123ab'
        @user.valid?
        expect(@user.errors.full_messages).to include('Password is too short (minimum is 6 characters)')
      end
      it 'passwordが存在してもpassword_confirmationが空では登録できない' do
        @user.password_confirmation = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Password confirmation doesn't match Password")
      end
    end
  end
end

before do ~ end

テストコードの最初のbefore do ~ endは、describe以下の各テストコードが実行される前に処理される共通の内容を記述します。
今回の場合は登録するユーザー情報を変数@userに代入する処理を記述しています。
これで各テストコードでユーザー情報が入った変数@userを使えるようになりました。

describeメソッド、contextメソッド、itメソッド

テストコードのグループ分けに使用するメソッドで、

  • describeメソッドはどのような機能なのか(テスト対象)
  • contextメソッドはどのような状況なのか
  • itメソッドは何をテストしているのか

をそれぞれグループ分けしています。

expectation(エクスペクテーション)

一番最初の「describe新規登録、context新規登録できるとき、it全ての項目が入力されていれば登録できる」の中にあるような以下のような記述の部分をexpectationといい、テストで得られた結果が期待通りなのかを確認する構文です。

expect(@user).to be_valid

上記の記述のうち、be_validの部分をmatcher(マッチャ)といいます。

matcher

「expectの引数(カッコの中の記述)」と「想定した挙動」が一致しているか判断します。
expectの引数にはテストで得られた実際の挙動を指定し、matcherにはどのような挙動を想定しているかを記述します

単体テストでよく使うmatcherの例

matcher 想定する挙動 記述例
include Xの中にYという文字列が含まれているか expect(X).to include(Y)
be_valid expectのインスタンスが正しく保存される expect(X).to be_valid

テストコードの実行

少し補足説明がなりましたが、上記のテストコードの実行をします。
ターミナルで以下のコマンドを入力します。

ターミナル
$ bundle exec rspec spec/models/user_spec.rb

# 実行結果
User
  新規登録
    新規登録できるとき
      全ての項目が入力されていれば登録できる
      passwordが6文字以上の半角英数字であれば登録できる
      identityが空で登録できる
    新規登録できないとき
      nameが空では登録できない
      emailが空では登録できない
      重複したemailが存在する場合登録できない
      emailに@が含まれていないと登録できない
      passwordが空では登録できない
      passwordが5文字以下では登録できない
      passwordが存在してもpassword_confirmationが空では登録できない

Finished in 0.07492 seconds (files took 1.3 seconds to load)
10 examples, 0 failures

上記ではエラーなくテストが完了しましたが、expectationに記述されているmatcherのエラーメッセージなどに記述ミスがある場合は修正する必要があります。

テストコードの修正にはpry-railsというデバッグ用のgemを使用するのですが、それは別の記事で紹介したいと思います。

RSpecのシリーズ記事

第1回:RSpecの導入&関係の深いgemについて
第2回:RSpec:モデルの単体テストの記述例 ※この記事

参考資料

RSpec: Behaviour Driven Development for Ruby
Qiita:
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?