RSpecで一部のテストを特定のタイミングでだけ有効にする
もう RSpec 忘れました。対象は
- rspec-core 3.13
です。
やりたいこと
- heavy test を手元の通常の開発サイクルから除外したい
重いテストはきらいです。重いテストはきらいです。重くならざるを得ないのは分かります。でも自分が今書きたいのは重くないテストなんで、邪魔せんといて。
patternではない
Method: RSpec::Core::Configuration#exclude_pattern — Documentation for rspec-core (3.13.0)
最初に思いついたのは
- exclude_pattern
を使って特定のパターンにマッチするテストを除外するという方法だったんだけど、これはファイル名でマッチするので、ファイル名以外の任意の条件で除外するということはできない。
例えば同じパスの中に重いテストと重くないテストがあって、重くないテストは除外したくない場合には役に立たない。
filter_run_*
が正解っぽい
rspec-core/Filtering.md at main · rspec/rspec-core
rspec 実行時の filtering はいろんな書き方ができて、-t
オプションなど実行方法もいくつもあるんだけど、そんな複雑なことはしたくない。
- 普段は除外されていてほしい
- ただし、いざという時にはすべてテストしてほしい
これくらいが何も考えずに実現してくれたら嬉しい。いちいち起動オプションやフィルタ設定方法を調べたくない。そうすると、
- 平時は
config.filter_run_excluding
を利用して動作しないように - 何らかの条件を満たしたらこの設定を反映しないように
すればよい。
ということは大枠は以下のような感じだ。
RSpec.configure do |config|
unless <いざ>
config.filter_run_excluding <重いやつ>
end
end
例えば Ruby on Rails を利用していて以下のような js: true な System spec があったとする。
describe "SomeSystem", js: true do
end
この時、
- js: true なテストについては Selenium 経由でブラウザを動かして重いので除外したい
- ただし CI 上ではすべて実行したい
ということであれば以下のよう設定になる。
RSpec.configure do |config|
if ENV["CI"] != "true"
config.filter_run_excluding js: true
end
end
これは多くの CI/CD サービスで自動的に環境変数 CI
が true
という文字列になることを利用して、逆にそれが設定されていない場合は js: true なテストを除外するという設定になっている。
filter_run_excludingをもう少し詳しく
Method: RSpec::Core::Configuration#filter_run_excluding — Documentation for rspec-core (3.13.0)
Adds key/value pairs to the exclusion_filter. If args includes any symbols that are not part of the hash, each symbol is treated as a key in the hash with the value true.
- key/value pairs を受け付ける
- もし Hash の一部じゃない Symbol が現れたらそれは
true
という値を持っているものとして扱われる
また、複数の設定を書きたければ複数の filter_run_excluding を並べるようだ。
filter_run_excluding は中で FilterManager を使って一つずつ @exclusions に追加していくように書かれている。
module RSpec
module Core
class Configuration
def filter_run_excluding(*args)
meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering)
filter_manager.exclude_with_low_priority meta
static_config_filter_manager.exclude_with_low_priority Metadata.deep_hash_dup(meta)
end
なるほど。
module RSpec
module Core
class FilterManager
attr_reader :exclusions, :inclusions
def initialize
@exclusions, @inclusions = FilterRules.build
end
snip)
def exclude_with_low_priority(*args)
exclusions.add_with_low_priority(args.last)
end
なるほどなるほど。