井原(@ihara2525)です。
以下のようにElasticsearchを使って検索するアクションがあり、RSpecでこのコントローラスペックを書きたい!という場合にどうしようって話です。
class PostsController < ApplicationController def search @records = Post.search(params[:query]).page(params[:page]).records end end
Gemfileにelasticsearch-extensionsを追加します。バージョンは適当なもので。 このgemがデフォルトで9250ポートで立ち上がるテスト用のElasticsearchのクラスタを用意してくれるので、9200で立ち上がる開発用のクラスタとかぶりません。
Gemfile
group :test do gem 'elasticsearch-extensions', '0.0.15'
で、毎回立ち上げたり落としたりしていると時間がかかって仕方がないので、:elasticsearchなときだけ動かすようにします。
require 'elasticsearch/extensions/test/cluster' RSpec.configure do |config| config.before(:all, :elasticsearch) do Elasticsearch::Extensions::Test::Cluster.start(nodes: 1) unless Elasticsearch::Extensions::Test::Cluster.running? end config.after(:all, :elasticsearch) do Elasticsearch::Extensions::Test::Cluster.stop if Elasticsearch::Extensions::Test::Cluster.running? end end
Elasticsearchのクライアントが使うホストやポートは、環境変数なりで変えられるようにしておいた方が良いですね。
config/initializers/elasticsearch.rb
Elasticsearch::Model.client = Elasticsearch::Client.new(host: ENV['ELASTICSEARCH_HOST'], logs: Rails.env.development?, trace: Rails.env.development?)
僕はdotenvを使って、.env.testでテスト用の環境変数を設定するようにしています。 テスト用なのでlocalhostの9250番を見るようにしておきます。
.env.test
ELASTICSEARCH_HOST='localhost:9250'
コントローラのテストはこんな感じ。
spec/controllers/posts_controller_spec.rb
RSpec.describe PostsController, :elasticsearch do let(:page) { FactoryGirl.create(:page, content: 'ページ') } before do Post.__elasticsearch__.create_index! force: true Post.__elasticsearch__.refresh_index! page Post.import # Wait test cluster to index the created objects sleep 1 end after { Post.__elasticsearch__.client.indices.delete index: Post.index_name } context 'when the result exists' do it 'assigns the result to @results' do get :search, query: 'ページ' expect(assigns(:records)).to contain_exactly(page) end end end
最初に:elasticsearchを指定しているので、このテスト全体が動く前にテスト用のElasticsearchのクラスタが立ち上がり、終わる前にクラスタが終了します。
あとは、各テストの前でインデックスがつくられるようにbeforeにデータを突っ込む処理を書き、afterでつくられたインデックスを削除しています。つくったデータがインデックスに反映されるのを待つために1秒スリープしているのがダサいですが、仕方ないですかね。。
というわけで、RSpecでElasticsearchを使ったテストをするサンプルをあまり見つけられなかったので書いておきました!