Rspecを書いてみよう

初めてテストコードを書いたのでおさらいのためにまとめてみます。
RailsはMInitestというテスティングフレームワークを備えていますが今回は
以前作った簡単なCRUDアプリ
を元にRspecを導入してテストを書いてみました。
テストが必要なほどの機能はないのですが練習のため。
2つの違いはよくわかってませんがRspecの方が利用率は高いそうです。
ではまずは導入からやっていきます。

必要gemのインストール

必要なgemを4つインストールします。

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

group :test do
  gem 'capybara'
  gem 'webdrivers'
end

rspec-rails

テスティングフレームワーク
これがないと始まらない。
GitHub - rspec/rspec-rails: RSpec for Rails 5+

factory_bot_rails

テスト用のデータを簡単に作成できるgem。
GitHub - thoughtbot/factory_bot_rails: Factory Bot ♥ Rails

capybara

E2Eテスティングフレームワーク
ブラウザでテストしてくれる。
GitHub - teamcapybara/capybara: Acceptance test framework for web applications

webdrivers

調べたんですがあんまりわかってません。
capybaraとセットで使われるっぽい。
selenium-webdriverchromedriver-helperの代わりにこのwebdrivers1つでいいみたいです。
GitHub - titusfortner/webdrivers: Keep your Selenium WebDrivers updated automatically

bundle install

でインストールします。

諸々の設定

rails g rspec:install

とすると

Running via Spring preloader in process 63316
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

これらが作成されます。
.rspec--format documentationを追加するとテスト結果の表示が変わります。
お好みで設定しましょう。

rails_helper.rbの

Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

これのコメントアウトを外すとspec/support以下のファイルを読み込みます。
モジュールなどを作成する場合はsupportディレクトリを作成し、その中に置きます。
このアプリにログイン機能はまだないので以下は例です。

# support/system_helper.rb

module LoginModule
  def login(user)
    visit '/login'
    fill_in 'Name', with: user.name
    fill_in 'Email', with: user.email
    fill_in 'Password', with: '00000000'
    click_button 'Login'
  end
end

support/capybara.rbを作成して

RSpec.configure do |config|
  config.before(:each, type: :system) do
    driven_by :selenium_chrome_headless
  end
end

ドライバーの設定もしておきます。
selenium_chrome_headlessを指定しておくとテスト実行の度にブラウザを立ち上げずにテストしてくれます。

テスト用データの作成

rails g rspec:model user

このコマンドでmodels/user_spec.rbとfactories/user.rbが作成されます。

FactoryBot.define do
  factory :user do
    
  end
end

こちらがfactories/user.rbの中身です。
ここでテスト用のデータを作成します。 userモデルとスキーマのusersテーブルは

class User < ApplicationRecord
  validates :name, presence: true
  enum gender: { male: 0, female: 1, secret: 2 }
end
  create_table "users", force: :cascade do |t|
    t.string "name", null: false
    t.integer "gender", default: 0, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

こんな感じです。

FactoryBot.define do
  factory :user do
    name { 'test_name' }
    gender { 'male' }
  end
end

名前と性別を設定しておきました。
テストで使うデータの初期値を設定しておく、というイメージです。
また、rails_helper.rbに

RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

これを追加しておくと、テストでデータを作るときに

# 設定なし
user = FactoryBot.build(:user)
# 設定あり
user = build(:user)

このように省略できるので設定しておきます。

テストの作成

ようやくテストの作成です。
今回はバリデーションに関するテストを作ってみます。
先ほど作成したmodels/user_spec.rbに

require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'バリデーション' do
    it '全てのフォームが正常な場合に成功する' do
      user = build(:user)
      expect(user).to be_valid
      expect(user.errors).to be_empty
    end

    it 'Nameが空の場合に失敗する' do
      user = build(:user, name: nil)
      expect(user).to be_invalid
      expect(user.errors[:name]).to eq ['を入力してください']
    end
  end
end

2つのテストを書いてみました。

1つ目のテストを説明すると、
・user = build(:user)
factories/user.rbの情報を元にユーザーを作成しuserに代入
・expect(user).to be_valid
userがバリデーションを通ることを期待
・expect(user.errors).to be_empty
user.errorsが空であることを期待
となります。

2つ目だと
・user = build(:user, name: nil)
初期値を元にするがnameは空にする
・expect(user).to be_invalid
userがバリデーションに引っかかることを期待
・expect(user.errors[:name]).to eq ['を入力してください']
エラー文、「を入力してください」と表示されることを期待
となります。

それではテストを実行してみましょう。

bundle exec rspec

とすると

Image from Gyazo

通りました。

最後に

ほんとに簡単なところだけですがテストを作って実行するところまでできました。実装のコードは間違っていたらエラーが出ますが、テストはそもそもテストコードをちゃんと書かないと意味を成さない気がするので気を付けたいです。何か間違いなどありましたらコメントいただけると嬉しいです。
では今回もありがとうございました。

参考サイト

E2Eテスト
RSpecとminitestのおおまかな違い - Qiita
FactoryBotでテストデータ作成する方法 - Qiita
【Rails】『RSpec + FactoryBot + Capybara + Webdrivers』の導入&初期設定からテストの書き方まで | vdeep