Rails から Elasticsearch を使っているときのテストの書き方(elasticsearch-rails, RSpec)
みんな大好き babyshark では全文検索に Elasticsearch を使っています。テストの書き方について日本語情報が少ないようなので、僕はこういう感じで書いてますというのを共有します。
環境は下記のとおりです。
- Elasticserch 1.3.1
- analysis-kuromoji(ブラグイン)2.3.0
gem
searchable.rb
まず、先に Elasticsearch へ検索クエリをどうやって投げているのかを簡単に擬似コードで説明すると下記のような感じです。
ActiveSupport::Concern
を使って、Video モデルに search
というクラスメソッドを生やしています。
# app/models/concerns/searchable.rb module Searchable extend ActiveSupport::Concern included do include Elasticsearch::Model # 環境ごとにインデックス名を別にする index_name [Rails.application.engine_name, Rails.env].join('_') # ... end module ClassMethods def search(query, sort: nil, ...) search_definition = { query: { # ... } } __elasticsearch__.search(search_definition) end end end
spec_helper.rb
spec_helper.rb について、ポイントは 2つです。
- 各テストを独立させるために、毎回検索インデックスをクリアする
- 検索インデックスのクリアは Elasticsearch に関するテストに限定して実行する
# spec_helper.rb RSpec.configure do |config| config.before :each, elasticsearch: true do Video.__elasticsearch__.create_index!(force: true) end config.include FactoryGirl::Syntax::Methods # ... end
seachable_spec.rb
テストは下記のように書いています。
- テストデータ登録後、検索インデックスへの反映を待つ(データが insert されるとコールバックで Elasticsearch に POST するようにしている)
- 検索インデックスのクリアを実行するために
elasticsearch: true
を付ける
# spec/models/concerns/searchable_spec.rb require 'spec_helper' describe Searchable, elasticsearch: true do describe 'self.search' do let!(:video1) { create(:video, title: 'みつばちの悪戯 及川奈央', tags: [ create(:tag, name: '色っぽい'), create(:tag, name: '巨乳'), ], views_count: 100, ) } let!(:video2) { create(:video, title: '痴漢バスおとり捜査官 麻美ゆま', tags: [create(:tag, name: '凌辱')], views_count: 200, ) } before do # Elasticsearch のインデックスに反映されるのを待つ sleep 1 end context '検索キーワードが動画タイトルにマッチするとき' do let(:videos) { Video.search('悪戯').records } it '検索キーワードにマッチする動画が検索結果に含まれる' do expect(videos.include?(video1)).to be_truthy expect(videos.include?(video2)).to be_falsey end end context '検索キーワードがタグ名にマッチするとき' do let(:videos) { Video.search('色っぽい').records } it '検索キーワードにマッチする動画が検索結果に含まれる' do expect(videos.include?(video1)).to be_truthy expect(videos.include?(video2)).to be_falsey end end context '並び順を指定したとき' do let(:videos) { Video.search('', sort: 'views_count.desc').records } it '指定された並び順で検索結果が返ってくる' do expect(videos[0]).to eq video2 expect(videos[1]).to eq video1 end end # ... end end
まあそうだよねという感じですが、こういうのが延々続いています。
context '検索キーワードがタグ名の一部にマッチするとき' context '検索キーワードが 2つで、いずれも動画タイトルにマッチするとき' context '検索キーワードが 2つで、ひとつだけ動画タイトルにマッチするとき' context '検索キーワードが 2つで、ひとつが動画タイトル、ひとつがタグ名にマッチするとき' context '検索キーワードが 2つで、全角スペースで区切られているとき' context '検索キーワードが 3つのとき' context '検索キーワードが空白のとき' # ...
以上です。ではでは。
PR
ちなみに人妻動画サイトの h300 のほうでも Elasticsearch を使っています。