3
Help us understand the problem. What are the problem?

posted at

updated at

Rails RspecのテストをShoulda Matchersで簡単に書く

はじめに

現場ではRspecを使用してテストコードを書いています。
modelのテストを行うにあたって先輩に要件を確認していたところ、「Shoulda Matchersを使うと簡単だよ。」と教えてもらいました。
そもそもこれなんて読むんだ?そして何が美味しいんだ?
と思いながら調べてみましたので備忘録として残します。
誤り、おかしな点はぜひご指摘いただければと思います。

環境(私のPC)

Ruby 2.7.1
Rails 6.0.5

Shoulda Matchersとは

Shoulda Matchersとは長くて複雑かつつまずきやすいテストコードをワンライナーで記述することができるgemです。ワンライナーなので一行ということです。
え?そんなことができるの?ありえないでしょう。
半信半疑の思いを払拭すべく実際にやってみることにします。
(補足)
調べてみると「シュルダ マッチャーズ」と読むみたいですね。
スウェーデン語のようで日本語に訳すと「一致すべきです」となります。

前提条件

※すでにRspecがインストールされているものとします。

モデルの準備

次のようなモデルを作成します。
Image from Gyazo
モデルに次のようなバリデーションを追加します。

  • titleが空では登録できない
  • 最大文字数255文字までしか登録できない
tool.rb
class Tool < ApplicationRecord
  validates :title, presence: :ture, length: { maximum: 255 }
end

Shoulda Matchersのgemをインストール

Gemfileに下記のように記述し、bundle installします。

Gemfile
group :development, :test do
# ここから追記----------------------------ーーーーーーーーーーーーーーー
  gem 'shoulda-matchers',
    git: 'https://github.com/thoughtbot/shoulda-matchers.git',
    branch: 'rails-5'
# ここまで追記----------------------------ーーーーーーーーーーーーーーー
end 

spec/rails_helper.rbの末尾に下記を追加記述します。

spec/rails_helper.rb
# 省略
RSpec.configure do |config|
  # 省略
end
# ここから追記----------------------------
Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end
# ここまで追記----------------------------

これで準備はできました。

※Shoulda Matchersのgemをインストールできず、下のようなエラーが発生することがあります。(私がそうでした)

NoMethodError: undefined method `new' for BigDecimal:Class

こちらの記事を参考にgemを更新すると上手くいきましたので試してみてください。

実際にやってみる

今回検証したいものは次の3点です。

  • titleが空文字の場合、登録できないこと
  • titleが255文字以内の場合、登録できること
  • titleが256文字以上の場合、登録できないこと

では通常のRspecで記述していきます。

いつものRspec

通常のRspecの記述法であれば細かな違いはあるにせよ、以下のようになると思います。

tool_spec.rb
require 'rails_helper'

RSpec.describe Tool, type: :model do
  describe 'tool' do
    before do
      @tool = Tool.new(title: "シャープペンシル")
    end

    it '空文字の場合、登録できないこと' do
      @tool.title = ""
      @tool.valid?
      expect(@tool.errors[:title]).to include("can't be blank")
    end

    it '255文字の場合、登録できること' do
      @tool.title = "あ"*255
      expect(@tool).to be_valid
    end

    it '256文字の場合、登録できないこと' do
      @tool.title = "あ"*256
      @tool.valid?
      expect(@tool.errors[:title]).to include("is too long (maximum is 255 characters)")
    end
  end
end

テストを実行してみましょう。

bundle exec rspec spec/models/tool_spec.rb 

Image from Gyazo
テストは無事に成功しています。

Shoulda Matchersを使ってみる

Shoulda Matchersを使ってテストコードを書いてみます。

tool_spec.rb
require 'rails_helper'

RSpec.describe Tool, type: :model do
  describe 'tool' do
    it { is_expected.to validate_presence_of :title }
    it { is_expected.to validate_length_of(:title).is_at_most(255) }
  end
end

非常にさっぱりにすることができます。
確かにワンライナーで書くことができてますね。

it { is_expected.to validate_presence_of :title }

こちらが空かどうかを検証するテストコードです。

it { is_expected.to validate_length_of(:title).is_at_most(255) }

こちらが文字数が適切か検証するテストコードです。
Shoulda Matchersを使うことで、255文字以内のテストと256文字以上のテストをまとめられています。
また、最初にbefore end内で@toolを作成しておく必要もなくなりました。
検証結果は下のように表示されます。
Image from Gyazo

気になった点

個人的に2点気になりました。

  • テストコード及び検証結果を見て何をしているかの理解が難しい(可読性)
  • 開発が終了しているらしいが導入の影響はあるのか

どなたかいい解決法わかればぜひ教えてほしいです。

※追記
@daishiman さんに共有していただきました。
テストは非エンジニアの方もできるものでなければならないという観点からいけば通常テストの方が適していると言えますね!

おわりに

Shoulda Matchersを使ってシンプルに短くテストを書くことができました。
使い方をもっと工夫すればあらゆる場面で使用できそうです。
もっと学習を深めていきたいと思います。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
3
Help us understand the problem. What are the problem?