patorashのブログ

方向性はまだない

RSpecでモデルのエラー確認はof_kind?が便利

Rails 6からだけれど、モデルのエラー確認にof_kind?メソッドが使えるらしい。

最初は、伊藤さんのQiitaの記事を見て、be_addedメソッドを知った。これも便利そうだなぁと思っていたのだが、そのコメント欄にRails 6からof_kind?が加わったと書いてあった。

qiita.com

qiita.com

どちらもめっちゃ便利じゃないですか。

今までどうしていたか?

すごく昔は、エラー数だけカウントして済ましていたけれど、最近はcontain_exactlyメソッドを使っていた。

expect(subject.errors.details[:name]).to contain_exactly({ error: :blank })

これでもまぁ割と簡潔には書けているようには見える…けれど、contain_exactlyは完全一致しないといけないので、他の値が入ってくるようなケースだと辛い。

# valueとかcountとか書くのが、かなり面倒…
expect(subject.errors.details[:code_name]).to contain_exactly(
  { error: :blank },
  { count: 3, error: :too_short },
  { error: :invalid, value: nil },
)

今後はどうするか?

これを、of_kind?で書き換えると…

# valueやcountが不要になって簡潔!
expect(subject.errors).to be_of_kind(:code_name, :blank)
expect(subject.errors).to be_of_kind(:code_name, :invalid)
expect(subject.errors).to be_of_kind(:code_name, :too_short)

コード量が増えているようには見えますが、行コピーして最後だけ書き換えるだけなので楽です。

既存のコードを書き換える正規表現

いちいち手で書き換えていくのが面倒なので、正規表現を作りました。RubyMine上で置換するのに使ったらちゃんと動きました。

# 検索条件
expect\(subject.errors.details\[(.+?)\]\)\.to contain_exactly\(\{.*?error: ([:\w]+)(.*?) \}\)
# 置換する文字列
expect(subject.errors).to be_of_kind($1, $2)

単純なやつ

Before

これが…

expect(subject.errors.details[:name]).to contain_exactly({ error: :blank })
After

こう置換されます。

expect(subject.errors).to be_of_kind(:name, :blank)

ちょっと複雑なやつ

Before

これが…

expect(subject.errors.details[:name]).to contain_exactly({ error: :invalid, value: nil })
After

こう置換されます。

expect(subject.errors).to be_of_kind(:name, :invalid)

まとめ

of_kind?メソッドを使うと検証が簡潔になっていい!ただし、Rails 6から!

あと、kind_ofと間違えがちになるので気をつけたいところ…。