Brad Rice
Brad Rice

Reputation: 1344

Rspec testing a controller search method

I'm trying to test the behavior of a custom search method in my controller:

    #RecordingsController
    def search
      # raise params.inspect
      @search = params[:search]
      searches = []
      searches2 = []
      for n in 1..5
        searches << @search["date(#{n}i)"].to_i
        searches2 << @search["date2(#{n}i)"].to_i
      end
      start_date = date_format(*searches)
      end_date = date_format(*searches2)
      conditions = []
      conditions << "agent like '%#{@search["agent"]}%'" unless @search["agent"].blank?
      conditions << "phone like '%#{@search["phone"]}%'" unless @search["phone"].blank?
      conditions << "date between '#{start_date}' and '#{end_date}'"

      @recordings = Recording.where(conditions.join(" AND ")).order('date ASC')
      if @recordings.blank?
        redirect_to("/", alert: "No results were found for this search. Please try again.")
      else
        render "recordings/search"
      end
    end

using the following layout:

    #recordings_controller_spec.rb
    describe RecordingsController do
      describe "POST #search" do
        context "with valid attributes" do
          it "assigns a new search to @search" do
            search = @recording_search
            get :search, @recording_search
            assigns(:search).should eq(search)
          end
          it "populates an array of recordings"
          it "renders the :search view"
        end
      end
    end

The furthest I've gotten is trying to build a hash that mimics what my params hash would be for the form

    #params hash
    params = {"search" => { "date_1i" => "2012", "date_2i" => "1", ... "date2_5i" => "00" } }

where date_#{n}i is the start date [year, month, day, hour, minute], and date2_#{n}i is the end date. I'm trying to follow the answer posted here, mimicking the params hash with just a regular hash. As you can see from my controller, I don't actually pass parameters to my #search method. Should I be? Or is there a way to mock a params hash in an rspec test and determine if my @search, @recordings, and redirect_to/render variables/actions are being performed? I'm already kind of testing the render/redirect in my request spec, but I'd like to fully test this method if I can.

Upvotes: 1

Views: 5436

Answers (1)

Winfield
Winfield

Reputation: 19145

You should be able to generate a controller spec that GETs the search action with a given set of parameters. This will cause those parameters to be available to the params hash. You can then verify how the search is constructed and which results are returned.

describe RecordingsController do
  describe '#search' do
    it 'should return results' do
      get :search, "search" => { "date_1i" => "2012", "date_2i" => "1", ... "date2_5i" => "00" }
      response.should be_ok
      @recordings.map(&:name).should == ['expected1', 'expected2']
    end
  end
end

This example executes a search with some search criteria as query parameters, verifies the response is successful (http 200 status), and then extracts the list of recordings returned and tries to map them to a list of friendly recording names (you can use any key on this model) to compare them to an expected list of results.

It'll be easier to write/test this code if you separate the concerns in this controller - you could write a helper that processes the query parameters and builds a search filter, which it then passes to the Recording model in the controller:

class RecordingController
  def search
    @search_filter = SearchFilter.for_params(params[:search])
    @recordings = Recording.where(@search_filter).order('date ASC')
    render "recordings/search"
  end
end

class SearchFilter
  # Return search params as a hash for given request params hash
  def self.for_params(params)
    ...
  end
end

This would let you write a unit test for the logic that generates search filters and only verify that the controller is doing the more simple operation of passing information between the search logic and the Recording model collection. I'd also recommend moving your logic about displaying empty results into the view on the results page and out of the controller.

Upvotes: 2

Related Questions