Integration Testing Rails Models with Tire

karmi edited this page Oct 12, 2011 · 1 revision

This page is a little light on narrative, but hopefully stands as a useful resource for testing Rails models with

class Widget
 
  include Tire::Model::Search
  include Tire::Model::Callbacks
 
  #
  # Define the name based on the environment
  # We wouldn't like to erase dev data during
  # test runs!
  #  
  index_name("#{Rails.env}-#{Rails.application.class.to_s.downcase}-widgets")
 
  #
  # mapping do; end
  #
  # No mapping here, because we need custom settings, 
  # we shouldn't use the mapping DSL here.
  #
 
  class << self
 
    def create_search_index
      Tire.index(Widget.index_name) do
        create(
          :settings => {
            "analysis" => {
              "analyzer" => {
                "widget_name_analyzer" => {
                  "type"      => "custom",
                  "tokenizer" => "lowercase",
                  "filter"    => ["name_ngram"]
                }
              },
              "filter" => {
                "name_ngram" => {
                  "type"     => 'edgeNGram',
                  "min_gram" => 2,
                  "max_gram" => 7,
                  "side"     => "front"
                }
              }
            }
          },
          :mappings => {
            :show => {
              :properties => {
                :id       => { :type => :integer                                     },
                :name     => { :type => :string,  :analyzer => :widget_name_analyzer }
              }
            }
          }        
        )
      end
    end
 
    def delete_search_index
      search_index.delete
    end
 
    def search_index
      Tire.index(Widget.index_name)
    end
    
  end 
end

The definition here is a little extensive, but effectively creates a simple mapping, where the name field of our Widget is analyzed at search time using the newly defined widget_name_analyzer.

Important to note the following:

  • We have class methods for creating, and deleting the index… this allows us to effectively recreate the index at test time.
  • The mapping is done inside of the index creation, we cannot use the mappings DSL when creating custom analyzers.
  • The index name uses the rails environment, as well as the application class name (only available to Rails)

Typically, in a Rails application then one would have an initializer such as:

Tire.configure do
  logger Rails.root + "log/tire_#{Rails.env}.log"
end

Widget.create_search_index unless Widget.search_index.exists?

Note: In production, your search index will be valuable, you don't want an initializer that empties your index, so we guard the creation of the index here with a check for it's existence. One could easily come up with a Rake task, or similar for re-creating an index, when the settings change in your production environment.

And, a simple test case:

require 'test_helper'
class WidgetSearchTest < ActiveSupport::TestCase

  setup do
    Widget.create_search_index
    #
    # Setup widgets for testing here
    #
    Widget.search_index.refresh
  end

  teardown do
    Widget.delete_search_index
  end

  def test_something_about_widgets
    ws = Widget.search("test")
    #
    # Normal assertions here, remember to refresh
    # the search index after creating new
    # records. At this point, using the Tire API
    # should be exactly like documented
    #
  end

end