Rails/RSpecでテストを書いていて、Elasticsearchを使った検索を盛り込んだテストを書く必要があったのでここにメモ。
動作環境:
- Ruby: 2.1.2p95
- Rails: 4.0.5
- RSpec: 3.0.4
- Elasticsearch: Version: 1.1.1
1) gem追加
これらのgemがないとそもそも無理っぽい
gem 'elasticsearch-rails'
gem 'elasticsearch-extensions'
2) spec_helper.rbにElasticsearchを使うときの処理を入れる
Elasticsearchは(確か)デフォルトで9200のポートを使うので、テストでは別のポートを割り当ててテスト用のElasticsearchサーバを立てるようにする。
毎回Elasticsearchサーバが立ち上がるととても重たいので、
elasticsearch: true
のメタタグをつけたブロックのみで有効になるようにしている
/spec/spec_helper.rb
require 'elasticsearch/extensions/test/cluster'
Elasticsearch::Model.client = Elasticsearch::Client.new(host: "localhost:9250")
# other configs
# elasticsearch config
config.before(:each, elasticsearch: true) do
unless Elasticsearch::Extensions::Test::Cluster.running?(on: 9250)
Elasticsearch::Extensions::Test::Cluster.start port: 9250, nodes: 1
end
User.__elasticsearch__.create_index! force: true
User.__elasticsearch__.refresh_index!
end
at_exit do
if Elasticsearch::Extensions::Test::Cluster.running?(on: 9250)
Elasticsearch::Extensions::Test::Cluster.stop port: 9250
end
end
3) モデルスペックに検索のテストを追加
あらかじめモデルのほうにはElasticsearchを使った検索時に:nameカラムがひっかかるようにしておく
以下のように、Userモデルでは:nameカラムでの検索が有効になることをテストする
/spec/models/user_spec.rb
let(:user) { create(:user) }
describe ':search_matching_user', elasticsearch: true do
before do
user.__elasticsearch__.index_document
Elasticsearch::Model.client.indices.flush
end
context 'nameで検索できる' do
let(:user) { create(:user, name: "John Doe") }
subject { User.search_match_customer_organizations("John").records.to_a }
it { is_expected.to eq [user] }
end
end
こんな感じ。
これでElasticsearchを使った検索を含むfeatureテストも書けるようになる。
Elasticseachを噛ませたテストが走る際、テスト用のElasticsearchサーバが毎回立ち上がるので実行には多少時間がかかるで、多用するとテストが遅くなる。
it 'ユーザを登録してそのユーザを検索する', js: true, elasticsearch: true do
visit new_user_path
fill_in 'user_name', with: 'Foo Bar'
fill_in 'user_email', with: 'foo@bar.com'
click_button 'Submit'
expect(page).to have_content('Foo Bar')
expect(page).to have_content('foo@bar.com')
fill_in 'input_search', with: 'Foo'
find_button('Search').click
expect(find(".user_name")).to have_content('Foo Bar')
expect(find(".user_emai")).to have_content('foo@bar.com')
end