RSpec Testing Rails With Elasticsearch

When your app is growing and you need to implement more complex queries, you will try to put load away from your database. Over at gigmit, we saw really long database times (~2 seconds) for a rather complex but often needed query. So I searched for an appropriate solution. Normally I try to avoid adding a new service to our infrastructure because it needs more administrative and monitoring work, so I checked out Postgresql's view and materialized view functionality but wasn't really satisfied. Also our already in used Redis server seemed to be too much to code to get what we needed. I know Elasticsearch for quite some time now and luckily there is a relatively new gem on the block which is called elasticsearch-rails. Its the official supported gem and the successor of the tire gem which isn't maintained anymore. The development of the new gem is very active (they are doing a real persistence layer right now to use Elasticsearch as a full database replacement) and you should check it out and get involved, altough they are still far from a 1.0 release and there are still some rough parts. For me it was how we could integrate the Elasticsearch part in our testing infrastructure. So I asked them and got a few good responses. There are also some more advanced solutions mentioned like spinning up a test cluster, but we wanted to keep it simple in the beginning. This post covers how we did it for gigmit.

First, be sure to add the Rails environment to every index name in your models to avoid killing your dev data when testing.

class MyModel < ActiveRecord::Base
  include Elasticsearch::Model
  index_name ['gigmit', Rails.env, self.base_class.to_s.pluralize.underscore].join('_')
end

Next add this to your spec_helper.rb:

RSpec.configure do |config|
  # ... your code ...
  config.before(:each) do
    [MyModel, MyOtherModel].each do |model|
      model.__elasticsearch__.create_index!(force: true)
    end
  end
end

It means it will recreate all specified indexes and clean them all beforr every test. It's rather fast and we use Elasticsearch mostly everywhere now, so this is sufficent for us. But if you want to use this only for specific tests you could do

RSpec.configure do |config|
  # ... your code ...
  config.before(:each, elasticsearch: true) do
    [MyModel, MyOtherModel].each do |model|
      model.__elasticsearch__.create_index!(force: true)
    end
  end
end

and let the tests where you need it run like this:

it 'should search with es', :elasticsearch do
  # your test code
end

Thats it. You now have a working setup for using Elasticsearch while testing with RSpec. Just be sure to have a running Elasticsearch node on your machine.