elastic / elasticsearch-rails

Elasticsearch integrations for ActiveModel/Record and Ruby on Rails
Apache License 2.0
3.07k stars 797 forks source link

Correct setup for rSpec #988

Open exocode opened 3 years ago

exocode commented 3 years ago

Hi there,

Can someone help me and confirm my approach or provide an actual (version 7.x) example how to setup a rSpec-environment for elasticsearch-rails (7.1.1) correctly? I am using Rails 6.1.x and rSpec 3.1.x.

I have doubt if my request tests are setup correctly, or if there is a better approach:

I saw examples with a Product.import and sleep 2 in specs, as well some real complex methods in the spec_helper.rb/rails_helper.rb file.

Currently I have this, it looks slim and works (till now):

rails_helper.rb

  # pass 'elasticsearch: true' or ':elasticsearch' to your tests to run this code
  config.around(:each, elasticsearch: true) do |example|
    Product.__elasticsearch__.create_index!(force: true)
    Product.import
    Product.__elasticsearch__.refresh_index!
    example.run
    Product.__elasticsearch__.client.indices.delete index: Product.index_name
  end

  config.after(:suite) do
    Elasticsearch::Model.client.indices.delete(index: '_all')
  end

product_request_spec.rb

  describe "by_name" do
    before(:each) do
      target = Fabricate :product, { name: term }
      Fabricate.times(3, :product)
      # Product.import(force: true) # sometimes it looks I have to use that
    end

    it 'respond with a pagination' do
      post "/search/by_name/#{term}"
      expect(response.body).to have_json_path("data")
    end
  end

Other approaches product_request_spec.rb

approach a.) (does not work always)

before(:each) do
  Product.__elasticsearch__.create_index! force: true
  Fabricate :product
  Product.__elasticsearch__.client.indices.refresh
end

approach b.) Works but very slow if test amount raise

before(:each) do
  Product.__elasticsearch__.create_index! force: true
  Fabricate :product
  Product.import(force: true)
  sleep 2 # but slowing down hundred of tests is not my favorite.
end

(maybe the docs can supplied with such an example... )

Thank you in advance

AndreiMotinga commented 3 years ago

@exocode Having the same issue. Seems like my current setup doesn't work properly, there are inconsitencies. I couldn't find any documentation on integrating with rspec. Can somebody please suggest something?

exocode commented 2 years ago

just wanna keep that issue open

ritec commented 1 year ago

I also have the same issue... it seems like there is a way to use refresh:wait_forbut I am not sure how exactly to use it.

alexander-makarenko commented 3 months ago

I was able to get a simple and reliable setup by:

  1. Adding an initialize to force the use of the refresh setting in the test environment, as I described here.

  2. Adding the following hook to my rails_helper.rb:

    RSpec.configure do |config|
     config.before do |example|
       next unless example.metadata[:elasticsearch]
    
       Elasticsearch::Model.client.cat.indices(format: "json").pluck("index").each do |name|
         Elasticsearch::Model.client.indices.delete(index: name)
       end
    
       Elasticsearch::Model::Registry.all.each do |model_klass|
         model_klass.__elasticsearch__.create_index!
       end
     end
    end

With this setup, any example tagged with :elasticsearch, e.g.:

it "does something", :elasticsearch do
  # ...
end

will start with a fresh set of Elasticsearch indices created based on your model register, which largely mimics the standard behaviour with the test database, where changes are rolled back after every example to ensure that there are no dependencies.

This setup allows me to write complex tests with records instantiated from multiple searchable models without the need to call any API methods to create, refresh or delete indices on invididual model classes.

a-gradina commented 3 months ago

@alexander-makarenko After adding your code I got WebMock::NetConnectNotAllowedError.

So right after if Rails.env.test? inside config/initializers/elasticsearch.rb I added this piece.

require "webmock"

WebMock.disable_net_connect!(allow_localhost: true)

Works now. Thanks!