概要
RailsでSolrが楽に使えるライブラリ、 sunspot_rails
の検索結果をRSpecで偽装するためにstubを使うやりかた
だいたい以下のバージョン
- Ruby 2.2.0
- Rails 4.2.3
- RSpec 3.3.2
- Sunspot 2.2.0
前提
公式によると、以下のようにしましょうってことなので、まずはコレに従う。
require 'sunspot/rails/spec_helper'
RSpec.configure do |config|
config.before(:each) do
::Sunspot.session = ::Sunspot::Rails::StubSessionProxy.new(::Sunspot.session)
end
config.after(:each) do
::Sunspot.session = ::Sunspot.session.original_session
end
end
実例
RSpec::Mocks.with_temporary_scope do
search = Hoge.search
record = FactoryGirl.build(:hoge)
# Hogeは適当に置き換えましょう。 FactoryGirlじゃなくfixture使ってたらそっちで。 scoreの数字は何でもいい。
raw_hit = {"id"=>"#{record.class.to_s} #{record.id}", "score"=>7.265403}
hit = Sunspot::Search::Hit.new(raw_hit, nil, search)
hit.instance_variable_set(:@result, record)
allow_any_instance_of(Sunspot::Rails::StubSessionProxy::Search).to receive(:hits).and_return([hit])
allow_any_instance_of(Sunspot::Rails::StubSessionProxy::Search).to receive(:results).and_return([record])
# 何かsunspotに依存した処理をして・・
YourSearchManager.search_hoge
# あとは自由にexpectationを書こう!
end
each_hit_with_results
を使いたければ rails_spec
の適当なところに以下のコードを書いておくとよいです。
class Sunspot::Rails::StubSessionProxy::Search
def each_hit_with_result
return unless block_given?
hits.each do |hit|
yield(hit, hit.result)
end
end
end
こんな感じで書くと良さげでした。 RSpec::Mocks.with_temporary_scope
がないと、同じスコープ内で別のitemをテストしようとするとコケるし、イディオム的に入れておきました。
このブロック内だけのシンプルなexpectationなら、外しちゃっても良いでしょうね。
allow_any_instance_of
がグローバル過ぎて気持ち悪いって人は、適当な所でメソッドを切り分けて、代わりに以下のようにしてstub化したsearchを返すようにするとよいです。(僕はそうしてる)
allow(search).to receive(:hits).and_return([hit])
allow(search).to receive(:results).and_return([hoge])
expect(YourSearchManager).to receive(:search_hoge).and_return(search)