davideisinger.com

My personal website
Log | Files | Refs | README

index.md (4112B)


      1 ---
      2 title: "Testing Solr and Sunspot (locally and on CircleCI)"
      3 date: 2018-11-27T00:00:00+00:00
      4 draft: false
      5 canonical_url: https://www.viget.com/articles/testing-solr-and-sunspot-locally-and-on-circleci/
      6 ---
      7 
      8 I don't usually write complex search systems, but when I do, I reach
      9 for [Solr](http://lucene.apache.org/solr/) and the awesome
     10 [Sunspot](http://sunspot.github.io/) gem. I pulled them into a recent
     11 client project, and while Sunspot makes it a breeze to define your
     12 search indices and queries, its testing philosophy can best be
     13 described as "figure it out yourself, smartypants."
     14 
     15 I found a [seven-year old code
     16 snippet](https://dzone.com/articles/install-and-test-solrsunspot) that
     17 got me most of the way, but needed to make some updates to make it
     18 compatible with modern RSpec and account for a delay on Circle between
     19 Solr starting and being available to index documents. Here's the
     20 resulting config, which should live in `spec/support/sunspot.rb`:
     21 
     22 ```ruby
     23 require 'sunspot/rails/spec_helper'
     24 require 'net/http'
     25 
     26 try_server = proc do |uri|
     27   begin
     28     response = Net::HTTP.get_response uri
     29     response.code != "503"
     30   rescue Errno::ECONNREFUSED
     31   end
     32 end
     33 
     34 start_server = proc do |timeout|
     35   server = Sunspot::Rails::Server.new
     36   uri = URI.parse("http://0.0.0.0:#{server.port}/solr/default/update?wt=json")
     37 
     38   try_server[uri] or begin
     39     server.start
     40     at_exit { server.stop }
     41 
     42     timeout.times.any? do
     43       sleep 1
     44       try_server[uri]
     45     end
     46   end
     47 end
     48 
     49 original_session = nil # always nil between specs
     50 sunspot_server   = nil # one server shared by all specs
     51 
     52 if defined? Spork
     53   Spork.prefork do
     54     sunspot_server = start_server[60] if Spork.using_spork?
     55   end
     56 end
     57 
     58 RSpec.configure do |config|
     59   config.before(:each) do |example|
     60     if example.metadata[:solr]
     61       sunspot_server ||= start_server[60] || raise("SOLR connection timeout")
     62     else
     63       original_session = Sunspot.session
     64       Sunspot.session = Sunspot::Rails::StubSessionProxy.new(original_session)
     65     end
     66   end
     67 
     68   config.after(:each) do |example|
     69     if example.metadata[:solr]
     70       Sunspot.remove_all!
     71     else
     72       Sunspot.session = original_session
     73     end
     74 
     75     original_session = nil
     76   end
     77 end
     78 ```
     79 
     80 *(Fork me at
     81 <https://gist.github.com/dce/3a9b5d8623326214f2e510839e2cac26>.)*
     82 
     83 With this code in place, pass `solr: true` as RSpec metadata[^1]
     84 to your `describe`, `context`, and `it` blocks to test against a live
     85 Solr instance, and against a stub instance otherwise.
     86 
     87 ## A couple other Sunspot-related things
     88 
     89 While I've got you here, thinking about search, here are a few other
     90 neat tricks to make working with Sunspot and Solr easier.
     91 
     92 ### Use Foreman to start all the things
     93 
     94 Install the [Foreman](http://ddollar.github.io/foreman/) gem and create
     95 a `Procfile` like so:
     96 
     97 ```
     98 rails: bundle exec rails server -p 3000
     99 webpack: bin/webpack-dev-server
    100 solr: bundle exec rake sunspot:solr:run
    101 ```
    102 
    103 Then you can boot up all your processes with a simple `foreman start`.
    104 
    105 ### Configure Sunspot to use the same Solr instance in dev and test
    106 
    107 [By
    108 default](https://github.com/sunspot/sunspot/blob/3328212da79178319e98699d408f14513855d3c0/sunspot_rails/lib/generators/sunspot_rails/install/templates/config/sunspot.yml),
    109 Sunspot wants to run two different Solr processes, listening on two
    110 different ports, for the development and test environments. You only
    111 need one instance of Solr running --- it'll handle setting up a
    112 "core" for each environment. Just set the port to the same number in
    113 `config/sunspot.yml` to avoid starting up and shutting down Solr every
    114 time you run your test suite.
    115 
    116 ### Sunspot doesn't reindex automatically in test mode
    117 
    118 Just a little gotcha: typically, Sunspot updates the index after every
    119 update to an indexed model, but not so in test mode. You'll need to run
    120 some combo of `Sunspot.commit` and `[ModelName].reindex` after making
    121 changes that you want to test against.
    122 
    123 ------------------------------------------------------------------------
    124 
    125 That's all I've got. Have a #blessed Tuesday and a happy holiday
    126 season.
    127 
    128 [^1]: e.g. `describe "viewing the list of speakers", solr: true do`