Posted at

RSpec3の新しいFormatter APIについて

More than 5 years have passed since last update.

いよいよRSpec3時代が近付いてきました。

betaとかrcで新しいバージョンが出る度にハマり所を仕込んでくるので、そろそろ落ち着いて欲しい所です。

RSpec3は表記の変更だけでなくて内部実装をがっつりリファクタリングしてるため、Formatterの作りも結構変わっています。

カスタムFormatterを自分で作ってる人とかForkして改造する人以外にはあんまり関係無いかもしれませんが、一応どう変わったのか書いておきます。

(そもそもそういう人は自分でソース読むし……)

Formatterの基本となるクラスはRSpec::Core::Formatters::BaseFormatterというクラスです。

これは2系でも3系でも変わってません。

RSpec3系で大きく変わったのは、Formatters.registerというメソッドを呼び出してFormatterクラスを登録しなければいけなくなった点です。

class BaseFormatter

# all formatters inheriting from this formatter will receive these notifications
Formatters.register self, :start, :example_group_started, :close

引数になってるのはクラスを表す定数と、引っ掛けるイベントの名前です。

必要無いイベントは無視できるようになったのがありがたいですね。

そして、今までFormatterには直接exampleの情報や実行時間が渡されていました。

2.14だとこんな感じ。

# BaseTextFormatter

def dump_summary(duration, example_count, failure_count, pending_count)
super(duration, example_count, failure_count, pending_count)
dump_profile unless mute_profile_output?(failure_count)
output.puts "\nFinished in #{format_duration(duration)}\n"
output.puts colorise_summary(summary_line(example_count, failure_count, pending_count))
dump_commands_to_rerun_failed_examples
end

これが3系だと一旦通知情報をまとめるNotificationというクラスにカプセル化されて渡されます。

そのため表示の整形なんかに利用していたメソッド全般がこっちに移っています。

なのでカスタムFormatterを作っていてFormatterクラスに整形のメソッドがあると思って呼び出しを行っているとぶっ壊れます。

3.0.0.rc1だとこんな感じ

# BaseTextFormatter

def dump_summary(summary)
output.puts summary.fully_formatted
end

超簡素になってますね。

ちなみにNotificationは通知の種類ごとにいくつか種類があり、それぞれ必要な情報を詰めてメソッドを生やしたStructのサブクラスになっています。

RSpec3でカスタムFormatterを作る時には、このNotificationクラスを見ると、どんなメソッドが利用できるのかが大体把握できます。

しかし厄介なことに、この新しいFormatterのAPIは3.0.0.beta2と3.0.0.rc1の間で互換性がありません。

beta2の時点ではリファクタリングがまだ中途半端な状態でリリースされてたらしい。

3系を利用していても、自分がどのバージョンを利用しているか注意してください。

(rc1から大きく変わったりしないだろうな……とbkbkしている)

もしRSpec2系時代のFormatterを継続して利用したい場合は、rspec-legacy_formattersというgemを入れる事で対応できるはずです。

APIが全然違うので、2系と3系で両対応させようと思ったらバージョンで分岐してクラス差し替えるしか無さそう。