【Rails】テストの準備からモデル単体テストまで【RSpec】


はじめに

Railsのテストについて書きます。
RSpecを使ったモデルの単体テストです。バリデーションがうまく働くかどうかを確認することがメインとなります。

RSpec導入

まずはgemをインストールします。
Gemfileを編集し、bundleインストールしましょう。

Gemfile
group :development, :test do
  gem 'rspec-rails'
end

# web-consoleというgemがtest環境の中にある場合はdevelopmentのみに移しておく
group :development do
  gem 'web-console'
end
ターミナル
$ bundle install

続いてrspec用のファイルを用意します。

ターミナル
$ rails g rspec:install

これでいくつかファイルが追加されたと思います。

以下を追加されたファイルの.rspecに追記します。

.rspec
--format documentation

続いて必要なディレクトリを用意しておきます。

spec/modelsディレクトリを作成

続いてテストコードを書くファイルを用意したmodelsディレクトリの中に作成します。
今回はpost_spec.rbを作成することにします。
ファイル名はモデル名_spec.rbとしましょう。

これで準備はOKです。

テストコード記述

テストコードを書いてみます。

spec/models/post_spec.rb
require 'rails_helper' # rails_helperを使うために必要です。何も考えず書いてください
describe Post do # Postモデルについて
  describe '#create' do # createアクションについて
    it "contentがない場合は登録できないこと" do # テストの確認内容を記述
      post = Post.new(content: "") # Postモデルのインスタンス(データ)を作成
      post.valid? # インスタンスを保存する際に、バリデーションにより保存ができない状態であるかを確かめる
      expect(post.errors[:content]).to include("can't be blank") # エラー文に"can't be blank"が含まれるかどうかを判定
    end
  end
end

それぞれの処理はコメントアウトで書いたとおりですが、コンソールで一つずつ確かめてみるともう少し理解できると思うので試してみましょう!
ターミナルでコンソール画面を立ち上げてみましょう。

ターミナル
$ rails c

テストコードのit 〜 endを順番に実行してみます。


[1]ここでPostモデルのインスタンス(contentカラムが空)が作成されます。

[2]上で作成したインスタンスが保存に失敗すればfalseが返ってきます。
valid?やinvalid?メソッドについての詳しい話はここを参考にしてください。


[3]エラー文はerrorsメソッドを使って見ることができます。
@messageにエラーメッセージが出ています。
今回Postモデルには空を許さないバリデーションの他に、最低でも2文字というバリデーションも書いてあるので、その2つのエラーが出ています。

便利なツール(FactoryBot)

上の例で使ったPostモデルはカラムが少なく、テスト用のデータを作成するのが簡単でした。
しかし、名前やメールアドレス、パスワードなどの複数の情報を持つUserモデルなんかだと、いちいちデータを書いていくのが面倒です。

user = User.new(name: "hoge", email: "[email protected]", password: "xxxxxx")

このように書かなくてはいけなくなり、冗長なコードになってしまいます。

FactoryBotというgemを使えばデフォルトのデータを用意しておき、必要な箇所だけデータを変更して使えるので便利です。
導入方法、使い方を順番に書いていきます。

まずはgemを入れます。

Gemfile
group :development, :test do
  #省略
  gem 'factory_bot_rails'
end
ターミナル
$ bundle install

spec/factoriesディレクトリを作成します。

続いてデータを書くファイルを用意したfactoriesディレクトリの中に作成します。
今回はusers.rbを作成することにします。
ファイル名はモデル名の複数形.rbとしましょう。
書き方は以下のとおりです。

spec/factories/users.rb
FactoryBot.define do

  factory :user do
    nickname              {"neko"}
    email                 {"[email protected]"}
    password              {"nyannyan"}
    password_confirmation {"nyannyan"}
    profile               {"hello"}
  end

end

factory_botを使うことで、specファイルで簡単にインスタンスを作成できます。

user_spec.rb
user = FactoryBot.create(:user)

と記述するだけでusers.rbで用意したインスタンスを作成してくれます。

実はもう少しだけ簡単に書くことができるのでその設定もしておきましょう。

factory_botの記法の省略
rails_helperに以下のように追記します。

rails_helper.rb
RSpec.configure do |config|
  # 以下を追記
  config.include FactoryBot::Syntax::Methods
  # (省略)
end

これを使って、例えばUserモデルのテストを書いてみます。

user_spec.rb
require 'rails_helper'

describe User do
  describe '#create' do

    it "nikcnameがない場合は登録できないこと" do
      user = build(:user, nickname: "")
      user.valid?
      expect(user.errors[:nickname]).to include("を入力してください")
    end

    it "emailがない場合は登録できないこと" do
      user = build(:user, email: "")
      user.valid?
      expect(user.errors[:email]).to include("を入力してください")
    end

  end
end

このようにbuild(:モデル名)とすることでインスタンスを作成できます。
空データなど、factory_botで用意した値と違う値を使いたい場合は、build(:モデル名, カラム名: 値)とすればOKです。

テストの実行

テストを実行するにはターミナルからコマンドを入力するだけでOKです。

ターミナル
$ bundle exec rspec  


テストが通るとこんな感じで表示されます。
失敗すると通らなかったテストの数だけfeiluresの数字がカウントされ、エラー内容が表示されます。

ファイルを指定してテストを実行

上で書いたコマンドbundle exec rspecはspecファイル全てを実行してしまうので、すでに確認済みのテストがある場合は少し無駄が出てしまいます。

ターミナル
$ bundle exec rspec spec/ディレクトリ名/ファイル名(_spec.rb)

とすることでファイルを指定して実行できます。
今回使用したuserモデルのテストを例にすると以下のようになります。

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