How to test ElasticSearch in a Rails application (Rspec)
Prefixing your index-names for the current environment
You could set a different index-name for each environment (in your case: the test environment).
For example, you could create an initializer in
config/initializers/tire.rb
with the following line:
Tire::Model::Search.index_prefix "#{Rails.application.class.parent_name.downcase}_#{Rails.env.to_s.downcase}"
A conceivable approach for deleting the indexes
Assuming that you have models named Customer, Order and Product, put the following code somewhere at your test-startup/before-block/each-run-block.
# iterate over the model types
# there are also ways to fetch all model classes of the rails app automaticly, e.g.:
# http://stackoverflow.com/questions/516579/is-there-a-way-to-get-a-collection-of-all-the-models-in-your-rails-app
[Customer, Order, Product].each do |klass|
# make sure that the current model is using tire
if klass.respond_to? :tire
# delete the index for the current model
klass.tire.index.delete
# the mapping definition must get executed again. for that, we reload the model class.
load File.expand_path("../../app/models/#{klass.name.downcase}.rb", __FILE__)
end
end
Alternative
An alternative could be to set up a different ElasticSearch instance for testing on another port, let's say 1234. In your enviornment/test.rb you could then set
Tire::Configuration.url "http://localhost:1234"
And at a suitable location (e.g. your testing startup) you can then delete all indexes on the ElasticSearch testing-instance with:
Tire::Configuration.client.delete(Tire::Configuration.url)
Maybe you must still make sure that your Tire-Mapping definitions for you model classes are still getting called.
I ran into a quirky bug when deleting my elasticsearch index via tire in my rspec suite. In my Rspec configuration, similar to the Bits and Bytes blog, I have an after_each call which cleans the database and wipes out the index.
I found I needed to call Tire's create_elasticsearch_index method which is responsible for reading the mapping in the ActiveRecord class to set up the appropriate analyzers, etc. The issue I was seeing was I had some :not_analyzed fields in my model which were actually getting analyzed (this broke how I wanted faceting to work).
Everything was fine on dev, but the test suite was failing as facets were being broken down by individual words and not the entire multi word string. It seems that the mapping configuration was not being created appropriately in rspec after the index was deleted. Adding the create_elasticsearch_index call fixed the problem:
config.after(:each) do
DatabaseCleaner.clean
Media.tire.index.delete
Media.tire.create_elasticsearch_index
end
Media is my model class.
I ran into similar issues and here's how I solved it. Bare in mind that my solution builds on top of @spaudanjo solution. Since I'm using spork, I add this inside the spec_helper.rb
's Spork.each_run
block, but you may add this into any other each/before block.
# Define random prefix to prevent indexes from clashing
Tire::Model::Search.index_prefix "#{Rails.application.class.parent_name.downcase}_#{Rails.env.to_s.downcase}_#{rand(1000000)}"
# In order to know what all of the models are, we need to load all of them
Dir["#{Rails.root}/app/models/**/*.rb"].each do |model|
load model
end
# Refresh Elastic Search indexes
# NOTE: relies on all app/models/**/*.rb to be loaded
models = ActiveRecord::Base.subclasses.collect { |type| type.name }.sort
models.each do |klass|
# make sure that the current model is using tire
if klass.respond_to? :tire
# delete the index for the current model
klass.tire.index.delete
# the mapping definition must get executed again. for that, we reload the model class.
load File.expand_path("../../app/models/#{klass.name.downcase}.rb", __FILE__)
end
end
It basically defines it's own unique prefix for every test case so that there are no in indexes. The other solutions all suffered from a problem where even after deleting the index, Elastic Search wouldn't refresh the indexes (even after running Model.index.refresh
) which is why the randomized prefix is there.
It also loads every model and checks if it responds to tire
so that we no longer need to maintain a list of all of the models that respond to tire both in spec_helper.rb
and in other areas.
As this method doesn't "delete" the indexes after using it, you will have to manually delete it on a regular basis. Though I don't imagine this to be a huge issue, you can delete with the following command:
curl -XDELETE 'http://localhost:9200/YOURRAILSNAMEHERE_test_*/'
To find what YOURRAILSNAMEHERE
is, run rails console
and run Rails.application.class.parent_name.downcase
. The output will be your project's name.