Posted at

[RSpec] 特定の spec / context でのみモジュールを include したい場合の実装と検証


背景

下記の2点の要件があるテストを書こうとした

1. 特定の feature / context / describe ブロック内のみ ActiveJob::TestHelper を include して有効化したい。

2. そのブロック内でのみテスト対応した queue_adapter (:testアダプタ?) を利用し、それ以外ではデフォルト (当方の環境では :delayed_job) のままにしておきたい


問題

そこそこの規模のプロジェクトでテスト書いていたら、どこかで :delayed_job アダプタ前提のテストが実装されている可能性があるが、自分で新規に追加したテストでは job queue の検証をしたいため ActiveJob::TestHelper のメソッドを呼んだり queue_adapter:test に変えたくなった。

そのため他人が書いたコードに影響しないよう、適用範囲を絞ったモジュールインクルードと queue_adapter の変更をできる方法を探った。


結論


  1. module を mixin したいブロックで include してあげれば適用範囲がそのブロックに限定されるからOK


  2. ActiveJob::TestHelper を include すれば明示的に queue_adapter を切り替える必要がない。
    ActiveJob::TestHelper が適宜 queue_adapter を調整して queue テスト可能にしてくれる。


検証コード

https://gist.github.com/qpSHiNqp/5f877886120ae722f4273d1c40d7bbf0


modules_spec.rb

require 'spec_helper'

describe RSpec, 'module isolation' do
module Example1
def hoge
"example1"
end
end
module Example2
def hoge
"example2"
end
end

context '#include Example1' do
include Example1
it 'should respond to :hoge and return "example1"' do
expect(self).to respond_to(:hoge)
expect(hoge).to eq("example1")
end
end
context '#include Example2' do
include Example2
it 'should respond to :hoge and return "example2"' do
expect(self).to respond_to(:hoge)
expect(hoge).to eq("example2")
end
end
context '#include nothing' do
it 'should not respond to :hoge' do
expect(self).not_to respond_to(:hoge)
end
end
end

describe RSpec do
before { ExampleJob.perform_later }

context '#include ActiveJob::TestHelper' do
include ActiveJob::TestHelper

it 'should be available to call #have_been_enqueued' do
expect { expect(ExampleJob).to have_been_enqueued }.not_to raise_error
end

it 'should be available to call #assert_enqueued_jobs' do
expect(self).to respond_to(:assert_enqueued_jobs)
assert_enqueued_jobs 1
end

it 'should change queue_adapter to :test' do
expect(ActiveJob::Base.queue_adapter).to be_kind_of(ActiveJob::QueueAdapters::TestAdapter)
end
end

context 'not #include ActiveJob::TestHelper' do
it 'should not be available to call #have_been_enqueued' do
expect { expect(ExampleJob).to have_been_enqueued }.to raise_error(StandardError)
end

it 'should not be available to call #assert_enqueued_jobs' do
expect(self).not_to respond_to(:assert_enqueued_jobs)
expect{assert_enqueued_jobs(1)}.to raise_error(StandardError)
end

it 'should not change queue_adapter to :test' do
expect(ActiveJob::Base.queue_adapter).not_to be_kind_of(ActiveJob::QueueAdapters::TestAdapter)
end
end
end