Rails
RSpec

既存のRailsプロジェクトをRSpec 3.0にアップグレードする際の注意点 ~RSpec 3は怖くないよ!~

More than 3 years have passed since last update.

はじめに

とうとうRSpec 3が正式に公開されたので、早速手持ちのRailsプロジェクトをアップグレードしてみました。
アップグレードしたのはプライベートなプロジェクト4本とパブリックなプロジェクト2本の合計6本です。

この記事では実際にRSpec 3にアップグレードしてみて困った点や気付いた点をまとめてみます。

注意: この記事は2014年6月4日時点の情報です

この記事は2014年6月4日時点の情報です。
gemの最新バージョンや周辺ライブラリの対応状況が変化している可能性もあるので、アップグレードする際は適宜ネット上の最新情報を確認するようにしてください。

アップグレードの手順

手順はざっくりいうとこんな感じです。

  1. 現行のテストが全てパスすることを確認する
  2. rspec-railsのバージョンを2.99にアップグレードする
  3. rspecを実行して警告を確認する
  4. 警告を潰す
  5. rspec-railsのバージョンを3.0.1(執筆時点での最新バージョン)にアップグレードする
  6. rspecを実行して警告を確認する
  7. 警告を潰す

RSpec 3にアップグレード後、実行時エラーや警告が出なくなったら対応完了です。

アップグレード時のTipsや注意点

移行用のブランチを作って作業する

移行作業中に「あー、やっぱりダメだ!!」となる可能性もゼロではないので、RSpec 3へアップグレードする際は専用のブランチを作ってその中で作業するようにしましょう。

Transpecを使ってspecを自動変換

rspec-railsを2.99にアップグレードすると、RSpec 2系の動作を保ちつつ、RSpec 3では使えなくなる機能について警告が出力されます。
警告は手作業でプチプチと潰していってもいいのですが、大量に警告が出ている場合は手作業で修正していくのはあまりにも大変です。

そこで便利なのがTranspecというgemです。
これを使うと自動的にspecを書き換えてくれます。
超便利!!

具体的な使い方や実行時のオプションについてはこちらで確認してください。

infer_spec_type_from_file_location!オプションを指定する

従来はspecファイルが配置されているディレクトリから自動的にspecのタイプ(model, controller, feature等)を判別してくれましたが、RSpec 3ではこの機能がオフになっています。

なので、specファイルのそれぞれでspecのタイプを明示する必要があるのですが、普通に使うのであれば、従来のように自動判別してもらう方が便利だと思います。

引き続き配置ディレクトリに応じてspecタイプを自動判別させるためには、spec_helper.rbinfer_spec_type_from_file_location!オプションを付けます。

# spec_helper.rb
RSpec.configure do |config|
  config.infer_spec_type_from_file_location!
  # ...

追記: Transpecでspecのタイプを自動的に付与することも可能
@yujinakayama さんのコメントにもある通り、Transpecで自動的にspecのタイプ(メタデータ)を付与することも可能です。
ただ、個人的には従来通りの振る舞いを維持する方が好みだったので、ここではinfer_spec_type_from_file_location!オプションを付ける方法を紹介しました。

rspec-collection_matchers gemを追加する

従来は配列の個数を検証するために、expect(group.users).to have(3).itemsというような構文が標準で用意されていましたが、RSpec 3ではこの構文がなくなりました。

expect(group.users.size).to eq 3というように、愚直にeq等のマッチャを使って書くのがRSpec 3標準のスタイルなのですが、従来の記法も捨てがたいです。

そんなときはrspec-collection_matchers gemを追加してください。
このgemを使えば、have(x).itemsのような構文が引き続き使えるようになります。

# Gemfile
group :test do
  gem "rspec-collection_matchers"
  # ...

rspec-railsは3.0.1以上にアップグレードする

rspec-rails 3.0.0にはrails g rspec:installが実行できないという不具合があったみたいで、すぐに3.0.1がリリースされています。
Gemfileには少なくとも3.0.1以上を指定するようにしましょう。

# Gemfile
group :development, :test do
  gem "rspec-rails", "~> 3.0.1"
  # ...

stub_chainメソッドはRSpec 3アップグレード後に修正する

RSpec 2.99とTranspecを使えばRSpec 3で警告が出ることはほとんどないのですが、一点だけなぜか警告が出てくるパターンがありました。
それは以下のようにstub_chainメソッドを使っているパターンです。

UserMailer.stub_chain(:password_instruction, :deliver)

というわけで、僕は手作業で以下のように修正しました。

allow(UserMailer).to receive_message_chain(:password_instruction, :deliver)

追記: Transpecで自動的に修正することも可能
@yujinakayama さんのコメントにもある通り、stub_chainの警告はTranspecを使って自動的に修正することもできます。
ただし、その場合はRSpec 3にアップグレードしてからTranspecを再度実行します。

RSpec 2.99で修正されない理由はallow(obj).to receive_message_chain(:foo, :bar, :baz)の構文がRSpec 3からでないと使えないためだそうです。

RSpec本体よりも周辺のgemの対応状況の方がネックになりがち

RSpec本体やrspec-railsなど、コアとなるライブラリについてはしっかりとアップグレードパスが整備されているので、それほど苦労しないはずです。
実際のアップグレード作業で問題となってくるのはそうしたコアライブラリではなく、email_specやjson_specのような周辺ライブラリの対応状況です。

周辺ライブラリはgemspecでRspecのバージョンがRSpec 2系に固定されていたり、古い記法を使っているためにRSpec 3で警告が出力されたりする可能性があります。

こうした問題を解決するために考えられる選択肢は以下の通りです。

  1. gemの対応が完了するまでアップグレードを控える
  2. forkしたRSpec 3対応版のgemを探してそれを使う
  3. 自分でforkしてRSpec 3対応版に修正する
  4. 警告を無視して使い続ける(アップグレードだけはできる場合)
  5. アップデートできないgemを使っている箇所をpendingにする

僕は主に2のパターンで対応しました。
この場合、本体がRSpec 3に対応したら、忘れずに戻さないとダメですけどね。

GitHubリポジトリのIssueを見ると、RSpec 3で発生している問題の報告やRSpec 3対応版のpull requestが見つかったりします。

こうした情報を元にfork先のgemをGemfileで指定しました。

# Gemfile

group :test do
  gem 'json_spec', github: "lonelyplanet/json_spec"
  # ...

5.のpendingにするパターンも悪くない選択肢かもしれません。
skipではなくpendingにするのがポイントです。
RSpec 3のpendingはそこで実行を止めるのではなく、最後まで実行してエラーが出たらパスと見なすように挙動が変わっています。
今回のように「問題が起きてうまく動かない」というテストに目印を付けておくのに丁度良いです。

いずれにしても、xxx-specやxxx-rspecみたいなライブラリを使っている場合は、アップグレードを実施する前にRSpec 3の対応状況を確認した方が良いかもしれません。

(RubyMineユーザー向け) RubyMineのTest runnerも調子が悪くなる

これはRubyMineを使っている人限定です。
RSpec 3にすると、どうもRubyMine内で実行するTest runnerがエラーを出すようです。
仕方がないのでRubyMine側の対応が完了するまで、僕はターミナルを使ってテストを実行するようにしています。

もしかすると、このあたりが関連するIssueなのかもしれません。
RUBY-15407 Fix code insight to work with RSpec 3.0.0

RSpec 3は怖くないよ!

RSpec 3では新機能がたくさん取り入れられたり、多くの構文がdeprecatedになったりしています。

変更点が大量にあるので、それらを全部読んでいくと「うわ、RSpec 3怖い!!」ってなってしまいますが、そんなに心配しなくても大丈夫ですw

RSpec 3になって一番影響が大きそうな部分といえば、be_true / be_falsebe_truthy / be_falseyに変わるところぐらいじゃないかなーと思います。

それ以外のベーシックな構文についてはほとんど変更がありません。
なので、RSpec 3向けに特別な知識を仕入れないとテストが全く書けなくなるとか、そういうことはないはずです。

既存のプロジェクトがある場合は前述のTranspecが自動変換してくれるので、その変更内容を一通り確認すれば「RSpec 3ではどう書けばいいか」が大体つかめると思います。

RSpec 3の大量のChangelogを全部把握しておかないとRSpec 3に移行できないわけではないので、その点は心配しないでください。

まとめ

というわけで今回は自分の実際の経験を元に、既存のRailsプロジェクトをRSpec 3.0にアップグレードする際の注意点をまとめてみました。

周辺ライブラリの対応状況はまだちょっと気になりますが、RSpec本体についてはそれほど問題なく移行できると思います。

最初はなんだかんだで右往左往する可能性もあるので、作業時間は1時間ぐらいで見積もっておきましょう。
本数をこなせば10分~20分ぐらいで移行できるようになるはずです。

みなさんも一度トライしてみてください!

お知らせ: 「Everyday Rails - RSpecによるRailsテスト入門」でも移行の手順を説明しています

僕が翻訳した電子書籍、「Everyday Rails - RSpecによるRailsテスト入門」でもRSpec 2.99を利用した移行の手順(移行の準備)を詳しく説明しています。

また、近い将来RSpec 3向けに内容をリニューアルする予定もあります。
Everyday Railsの購入者なら誰でもリニューアルした内容を無料でダウンロードすることが可能です。

内容は非常に初心者向けで、「そもそもRSpec自体がよくわかってないんだよね~」という方には打ってつけの一冊です。

対応している電子書籍のフォーマットは、PDF、EPUB、Kindleの3種類です。
興味のある方はこちらのページからご購入ください。(よろしくお願いします!)

Everyday Rails - RSpecによるRailsテスト入門

Everyday Rails - RSpecによるRailsテスト入門