Search code examples
ruby-on-railsrspecrspec-railssearchkickrspec-mocks

How to stub Searchkick in search method in controller


I am new to RSpec. I want to test my search controller. I try to stub my search method in the controller but it always fails.

 Failure/Error: expect(Doctor).to receive(:search).with(search_param)
   (<Doctor(id: integer, name: string, address: string, phone: string, is_active: boolean, field_id: integer, created_at: datetime, updated_at: datetime, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, sign_in_count: integer, current_sign_in_at: datetime, last_sign_in_at: datetime, current_sign_in_ip: inet, last_sign_in_ip: inet, roles: integer, description: text, avatar_file_name: string, avatar_content_type: string, avatar_file_size: integer, avatar_updated_at: datetime, avatar_processing: boolean, title: string, valid_doctor: boolean, verification_photo_file_name: string, verification_photo_content_type: string, verification_photo_file_size: integer, verification_photo_updated_at: datetime) (class)>).search("rizky")
       expected: 1 time with arguments: ("rizky")
       received: 0 times

What's the correct way to isolate Searchkick?

Here is my search method in the controller:

def search
    @doctors = []
    keyword = params[:keyword]
    @doctors = call_search_in_doctor(keyword) if keyword.present?
    respond_to do |format|
      format.json { render json: @doctors, status: 200 }
      format.html { render 'users/search/index.html.haml', status: 200 }
    end
end

def call_search_in_doctor(keyword)
  Doctor.search keyword,
                misspellings: { edit_distance: 3 },
                page: params[:page],
                per_page: 10,
                limit: 100,
                fields: [{ name: :word },
                         { name: :word_start },
                         { name: :word_middle },
                         { name: :word_end },
                         :code, :field_name]
end

And here is my controller test:

context 'with keyword' do
  let(:search_param) { "rizky" }
  let(:doctor) { instance_double(Doctor) }
  let(:results) { instance_double(Searchkick::Results) }

  before do
    allow(Doctor).to receive(:search).with(search_param) {:results}
  end

  it 'calls Doctor.search' do
    get :search, search_param
    expect(Doctor).to receive(:search).with(search_param)
  end
end

Thanks for your precious time!


Solution

  • RSpec isn't saying that Doctor.search wasn't called, it's saying that it wasn't called with the arguments you specified. Your production code calls Doctor.search with two arguments (the search term and the hash of parameters), but your test allows Doctor.search to be called with only one argument (the search term).

    You can fix it either by

    • adding the second argument to the with calls in your test (if you think it's relevant to the test to check what that hash is), or
    • adding a second argument of anything to those calls (if you think the hash is irrelevant to the test and just want to ignore it)