並行で行っている RSpec のテストの実行数がなぜか実行ごとに異なるという問題が発生した。実行ログを調査したところ、テストの途中で RSpec が正常に終了していることがわかった。原因はテストコード中で exit
を実行していること。特に rake タスクのテストとかで結構あると思う。
例えば、このようなコードがあり
main.rb
def assert_some_conditions!(conditions)
if conditions == 1
exit
end
end
このようなテストを書いた時、
main_spec.rb
require 'rspec'
require './main'
RSpec.describe 'assert_some_conditions!' do
it 'be nil when 1(まちがい)' do
expect(assert_some_conditions!(1)).to be_nil
end
it 'be nil when 0' do
expect(assert_some_conditions!(0)).to be_nil
end
end
二つ目の it
が実行されないままテストが正常終了する!!
$ bundle exec rspec main_spec.rb -fd
assert_some_conditions!
Finished in 0.00046 seconds (files took 0.04432 seconds to load)
1 example, 0 failures
こんなふうになる。 exit
を通った時点で RSpec ごと終了するので何もテスト実行されてない。テストが増えてくると結構気づかないとおもう。怖い。並行テスト&ある程度ランダムな割り振り(たまたまCIが毎回の実行時間に基づいて並行を割り振ってたので微妙なランダム)で RSpec を走らせていたのでばらつきに気づいたけど、そうじゃなかったら気づかなかったかもしれない。
対処法
exit
は StandardError
ではない 例外 SystemExit
を発生させているので、これをキャッチして投げ直せばいい。
exit は例外 SystemExit を発生させることによってプログラムの実行を終了させますので、必要に応じて begin 節で捕捉することができます。
コンフィグに書いちゃえば安心かも
main_spec.rb
RSpec.configure do |config|
config.around(:example) do |example|
example.run
rescue SystemExit
fail '予期しない exit が発生しました'
end
end
これ書いておけばちゃんと失敗するし最後までテストが実行される。
$ bundle exec rspec main_spec.rb -fd
assert_some_conditions!
be nil when 1(まちがい) (FAILED - 1)
be nil when 0
Failures:
1) assert_some_conditions! be nil when 1(まちがい)
Failure/Error: fail '予期しない exit が発生しました'
RuntimeError:
予期しない exit が発生しました
# ./main_spec.rb:8:in `rescue in block (2 levels) in <top (required)>'
# ./main_spec.rb:6:in `block (2 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# SystemExit:
# exit
# ./main.rb:3:in `exit'
Finished in 0.00095 seconds (files took 0.04431 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./main_spec.rb:13 # assert_some_conditions! be nil when 1(まちがい)