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](https://i.gyazo.com/e799b7a5ace1a84b392d07d66d8c84d2.png)](https://gyazo.com/e799b7a5ace1a84b392d07d66d8c84d2)
モデルに次のようなバリデーションを追加します。
- 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
~~~

https://qiita.com/blackpeach7/items/fb131ce9ebdb6b47beec

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

# 実際にやってみる
今回検証したいものは次の３点です。
- 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](https://i.gyazo.com/00dc4bf53c416226473ee86bca067505.png)](https://gyazo.com/00dc4bf53c416226473ee86bca067505)
テストは無事に成功しています。

### 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
~~~
非常にさっぱりにすることができます。
確かにワンライナーで書くことができてますね。
~~~ ruby
it { is_expected.to validate_presence_of :title }
~~~
こちらが空かどうかを検証するテストコードです。
~~~ ruby
it { is_expected.to validate_length_of(:title).is_at_most(255) }
~~~
こちらが文字数が適切か検証するテストコードです。
Shoulda Matchersを使うことで、255文字以内のテストと256文字以上のテストをまとめられています。
また、最初にbefore end内で@toolを作成しておく必要もなくなりました。
検証結果は下のように表示されます。
[![Image from Gyazo](https://i.gyazo.com/d5d3a397258b8db138211b8c63d0606d.png)](https://gyazo.com/d5d3a397258b8db138211b8c63d0606d)

# 気になった点
個人的に２点気になりました。
- テストコード及び検証結果を見て何をしているかの理解が難しい（可読性）
- 開発が終了しているらしいが導入の影響はあるのか

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

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

https://speakerdeck.com/jnchito/number-vstat?slide=18


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

# 参考
https://www.miracleave.co.jp/contents/1487/rspec-shoulda-matchers/

http://www.code-magagine.com/?p=9781