LoginSignup
5
3

More than 3 years have passed since last update.

RSpecで時間周りの備忘録

Posted at

:lock: 時間の操作

Timecopを使って止めた時間の中でテストするのが良い.
Railsには ActiveSupport::Testing::TimeHelpersでも似たことができるが、ネストしてると期待通り動かないとか細かい点でTimecopの方がおすすめです.

ミリ秒を丸める

DB等でミリ秒が保持できない場合もあるのでテストが失敗することがある.
事前にミリ秒を丸めて扱っていた方が良い.

# Good
let(:now) { Time.current }

# Best
let(:now) { Time.current.change(usec: 0) }

Timecopの場合

# Bad
Timecop.freeze(Time.current)

# Best
Timecop.freeze
Timecop.freeze(Time.current.change(usec: 0))

⏳Timeout

RSpecを書いていると思いのほか時間がかかる処理が紛れ込んでしまう.
中には何かしらのタイミングで異常に時間がかかる処理もあったりする.

タイムアウトする様にすることで不安定なテストを特定することはできます.
ただし不安定なテストを改善するわけでじゃないので、無闇にタイムアウトの時間を伸ばすのはよくない.
後、実行環境が非力だとタイムアウトが多発するのにも注意が必要です.

require 'timeout'

it do
  # 5秒経過すると `Timeout::Error` がraiseされる
  Timeout::timeout(5) do
    expect{ subject.execute }.to_not raise_error
  end
end

メタデータで制御

テストが少ないうちは大して気にならないが、ある程度の数になるとCIの実行時間が問題になってくる.
そこでRSpecにタイムアウトを仕掛けられる様にした.

spec/support/timeout.rb に以下のコードを配置する


timeout.rb のコード

require 'timeout'

class RSpecTimeoutError < Timeout::Error
  def message
    'execution expired by RSpec'
  end
end

RSpec.configure do |config|
  rspec_timeout = ENV.fetch('RSPEC_TIMEOUT', 0).to_i
  config.around(:example) do |example|
    sec = example.metadata[:timeout] || rspec_timeout
    next example.run if sec <= 0
    Timeout::timeout(sec, RSpecTimeoutError) do
      example.run
    end
  end

  config.backtrace_exclusion_patterns << /#{Regexp.escape(Pathname.new(File.expand_path(__FILE__)).relative_path_from(Rails.root).to_s)}/
end


使い方

タイムアウトの指定は2種類あり、それぞれ値にはタイムアウトする秒を指定します.
ただし0を指定した時はタイムアウトは無効になります.

環境変数を指定

RSPEC_TIMEOUT を指定する事でタイムアウトが有効になります.
ローカル等の通常の環境ではタイムアウトはせず実行してもらいたいためです.

CI等の全てのテストを実行するような場合のみ環境変数を指定すると良いでしょう.

RSPEC_TIMEOUT=20 rspec

メタデータを指定

特定の箇所だけタイムアウトを制御したいときはメタデータを使います.

it('foo', timeout: 30) { ... }
5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3