| まえおき
本記事はRspecのカスタムフォーマッタークラスの作成に関する記事です。
| きっかけ
むかしむかしrubyのバージョンを1.8.7から1.9.2へアップしたいおじいさんがおりました。
おじいさんはGemfileやらDockerやらをこねくりまわして、どうにか新しいバージョンでrspecが動くところまでこぎつけましたが、その時点でrspecのエラー数は1000件を超えておったそうな。。。
これは長丁場になると見込んだおじいさんはグーグルスプレッドシートを使ってエラーの一覧を管理しようと考えましたが、1000件を越えるエラーの内容をスプレッドシートに記帳するのは大変な作業です。
おじいさんは、だめもとで飼犬のポチオウにこのことを相談してみました。
するとポチオウは言いました。「おじいさんおじいさん、RspecのエラーをTSVファイルに書き出しするRspecのカスタムフォーマッタークラスを作ればいいんじゃない?」
| バージョン
- ruby 1.9.3
- rails 3.0.1
- rspec 2.11.0
| rspecはこんな感じで実行する
$ bundle exec rspec -r ./spec/formatter/documentation_with_write_tsv_formatter.rb --format DocumentationWithWriteTsvFormatter
- オプション補足
- -r: 実行時に読み込み(require)されるファイルの指定
- --format: 使用するFORMATTERを指定するオプション
| 生成されるファイルはこんな感じ
パス
tmp/spec_failure.tsv
仕様
- 区切り: タブ形式
- 改行: LF
- 文字列内の「"」は「””」に変換(スプレッドシートでのエスケープ指定)
内容はこんな感じ
No. | コマンド | テスト概要 | Failure/Error | エラー種別 | エラー内容 | バックトレース | 処理開始日時 | 処理終了日時 | 処理時間(s) |
---|---|---|---|---|---|---|---|---|---|
1 | rspec ./spec/models/user_spec.rb:72 | User.create 新規作成 データが一件登録される | test = 12 | TypeError | can't convert nil into Integer | ./app/models/user.rb:24:in `block (2 levels) in create' | 2019-10-31 18:22:03 +0900 | 2019-10-31 18:22:04 +0900 | 1.3508351 |
| カスタムフォーマッタークラスはこんな感じ
どうやって作ったか
ググってもあまり情報がなかったので、coreクラスのソースファイルを照会しながらなんとか実装したんだとさ。
RSpec::Core::Formatters::BaseFormatter
RSpec::Core::Formatters::BaseTextFormatter
RSpec::Core::Formatters::DocumentationFormatter
設置場所
特に決まりはないみたいなので「spec/formatter」のディレクトリを作成してその中に格納しましたとさ。
spec/formatter/documentation_with_write_tsv_formatter.rb
ソースコード
RSpec::Core::Formatters::DocumentationFormatterを拡張しましたとさ。
require 'rspec'
require 'rspec/core/formatters/documentation_formatter'
class DocumentationWithWriteTsvFormatter < RSpec::Core::Formatters::DocumentationFormatter
EOL_LF = "\n"
HEADER = [
"No.",
"コマンド",
"テスト概要",
"Failure/Error",
"エラー種別",
"エラー内容",
"バックトレース",
"処理開始日時",
"処理終了日時",
"処理時間(s)"
]
def initialize(output)
super(output)
@group_level = 0
@failure_list = []
end
def failure_output(example, exception)
cmd = "rspec #{RSpec::Core::Metadata::relative_path(example.location)}" # rspecコマンド(行数つき)
failed_line = read_failed_line(exception, example).strip # fail発生箇所のソース
type = exception.class.name # fail発生箇所のソース
full_description = example.full_description # テストの条件
backtrace_text = format_backtrace(exception.backtrace, example).join(EOL_LF) # トレースバック
@failure_list.push [next_failure_index, cmd, full_description, failed_line, type, exception.message, backtrace_text, example.execution_result[:started_at], example.execution_result[:finished_at], example.execution_result[:run_time]] # エラー内容を配列に格納
red("#{current_indentation}#{example.description.strip} (FAILED - #{@next_failure_index})") # エラー出力も行う
end
def close
output_file()
end
private
def output_file
if @failure_list.count > 0
FileUtil.fwrite "#{::Rails.root}/tmp/spec_failure.tsv", text_convert_failure_list, false
end
end
def text_convert_failure_list
all_text = ""
@failure_list.unshift HEADER
@failure_list.each do |failure|
output = failure.map {|text|
# 文字列の場合は「”」を「""」に変換(スプレッドシート向けのエスケープ)
if text.kind_of?(String)
text = text.to_s.gsub('"', '""') #
text = "\"#{text}\""
end
text
}
array_string = output.join("\t")
all_text << "#{array_string}#{EOL_LF}"
end
all_text
end
end
使いかた
冒頭に記載したようにrspecコマンドにオプションを加えて実行すれば、tmp/spec_failure.tsvにエラー内容が書き込まれるそうな。
$ bundle exec rspec -r ./spec/formatter/documentation_with_write_tsv_formatter.rb --format DocumentationWithWriteTsvFormatter
おじいさんは「Jenkinsおじさん使い」だったそうで、作成されたファイルをジョブの成果物として結果画面から簡単にダウンロードできるようしたそうな。
グーグルスプレッドシートへの記帳も楽々だったらしく、その後は末長くポチオウと暮らしましたそうな。
おしまい
おわりに
少しでも楽しく書けるように昔話風にしてみました。
参考URL
- RSpec::Core::Formatters
- Rspec3 カスタムフォーマットの例(CSV出力)